Hessian是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 相比WebService,Hessian更簡單、快捷。採用的是二進制RPC協議,由於採用的是二進制協議,因此它很適合於發送二進制數據。——百度百科html
學習hessian,必須知道什麼是RPC。git
實現RPC,必須解決以下幾個問題:github
一、通信問題。web
二、尋址問題。spring
三、序列化與反序列化。apache
帶着這三個問題咱們一塊兒來探究一下hessian;api
首先,你們去下載源碼,並導入到idea中打開;網絡
https://github.com/zhaojiatao/learn_hessianmvc
項目結構:app
--learn_hessian
----api 客戶端和服務端均共同引用的接口api
----client 客戶端調用demo,包括本地直接經過代理對象調用以及使用spring集成方式調用
----webserver 經過傳統servlet方式提供服務
----webserver_spring 與spring集成 提供服務
在web.xml中配置HessianServlet,
指明服務地址:/hessian
及其實現類:com.zjt.learn.hessian.api.impl.GetUserInfoImpl
<servlet> <servlet-name>HessianServlet</servlet-name> <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class> <init-param> <param-name>service-class</param-name> <param-value>com.zjt.learn.hessian.api.impl.GetUserInfoImpl</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HessianServlet</servlet-name> <url-pattern>/hessian</url-pattern> </servlet-mapping>
package com.zjt.learn.hessian.api.impl; import com.zjt.learn.hessian.api.GetUserInfo; import com.zjt.learn.hessian.dto.User; import org.apache.commons.lang3.StringUtils; /** * Created by zhaojiatao@souche.com on 2018/4/17 */ public class GetUserInfoImpl implements GetUserInfo { @Override public String getuserinfo(String id) { if(StringUtils.isNotBlank(id)){ User user=new User(); user.setId("1"); user.setName("zhaojiatao"); user.setAddress("hangzhou"); user.setAge(18); user.setGender(1); return user.toString(); } return null; } }
首先在web.xml中配置springmvc:
<servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/remote/*</url-pattern> </servlet-mapping>
配置applicationContext.xml:
使用HessianServiceExporter來處理請求;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd" default-lazy-init="true"> <bean id = "getuserService" class="com.zjt.learn.hessian.api.impl.GetUserInfoImpl"/> <bean name="/getuserHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="getuserService"/> <property name="serviceInterface" value="com.zjt.learn.hessian.api.GetUserInfo"/> </bean> </beans>
package test; import com.caucho.hessian.client.HessianProxyFactory; import com.zjt.learn.hessian.api.GetUserInfo; /** * Created by zhaojiatao@souche.com on 2018/4/17 */ public class BasicClient { public static void main(String[] args) { try { String url = "http://localhost:8080/hessian"; HessianProxyFactory factory = new HessianProxyFactory(); factory.setOverloadEnabled(true); GetUserInfo getUserInfo = (GetUserInfo) factory.create(GetUserInfo.class, url); System.out.println(getUserInfo.getuserinfo("1")); System.out.println("over"); }catch (Exception e){ e.printStackTrace(); } } }
新建spring.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd" default-lazy-init="true"> <bean id="getuserService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl" value="http://localhost:8080/remote/getuserHessianService"/> <property name="serviceInterface" value="com.zjt.learn.hessian.api.GetUserInfo"/> </bean> </beans>
package test; import com.zjt.learn.hessian.api.GetUserInfo; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by zhaojiatao@souche.com on 2018/4/18 */ public class BasicSpringClient { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:spring.xml"}); GetUserInfo getUserInfo = (GetUserInfo)context.getBean("getuserService"); System.out.println(getUserInfo.getuserinfo("1")); } }
hessian的使用是很簡單的。你們本身照着代碼敲一下就能夠。
回到文章開始提出的問題,即rpc框架須要解決的問題,看看hessian如何解決的。
咱們先看看客戶端在發起遠程請求前都經歷了什麼:
首先,客戶端經過代理工廠對象HessianProxyFactory的create方法建立代理對象;
在create方法裏能夠看到,該代理對象在執行時的handler是經過HessianProxy代理對象的invoke方法來執行;典型的動態代理;
在invoke方法中:
經過String methodName = method.getName();獲得方法名
經過sendRequest方法取得和服務端的鏈接HessianConnection對象;
跟蹤sendRequest方法,咱們發現:
發現,hessian是使用http協議進行網絡通訊;
在is = getInputStream(conn);處等待服務端返回的響應;
咱們跟蹤這裏:
咱們得出結論:hessian使用lookup方法來尋找遠程服務;
咱們繼續看剛剛跟蹤的客戶端調用時執行的HessianProxy對象的invoke方法,
進入其中的
conn = sendRequest(mangleName, args);
再進入
out.call(methodName, args);
再進入
writeObject(args[i]);
進入其Hessian2Output實現中
最終看到了hessian如何進行序列化:
serializer.writeObject(object, this);
至此,咱們也能夠梳理一下hessian客戶端動態代理的執行流程:
咱們再來看看服務端的執行細節:
經過後臺的代碼,可見咱們全部的工做都圍繞在HessianServlet在展開。該Servlet中有兩個比較重要的方法:init()、service();
init方法初始化服務和服務對象,主要分爲3步:
經過home-class或者service-class建立服務端的實現類實例;
init方法還會建立HessianSkeleton對象,這是Hessian服務端的核心功能部分。
HessianSkeleton繼承自AbstractSkeleton,其構造方法,將會從實現類中抽取方法和方法的Method對象,而且存儲到_methodMap中。
對於一個Servlet來講其service方法是對外提供服務的方法:
最主要的是調用HessianSkeleton對象的invoke方法。注意,Servlet實例中有兩個HessianSkeleton變量,分別是:_objectSkeleton和 _homeSkeleton,
invoke方法:
首先從HessianInput對象中獲取到Method信息,獲取到真正的service對象。
根據反射機制,調用service對象的invoke方法,獲取到返回值。
最後調用HessianOutput對象將結果寫回到調用方。
本文參考:
https://blog.csdn.net/sunwei_pyw/article/details/74002351
https://www.cnblogs.com/happyday56/p/4268249.html