Hessian解析及應用(整合Spring)


        Hessian是一個輕量級的remoting onhttp工具,使用簡單的方法提供了RMI的功能。 相比WebService,Hessian更簡單、快捷。------來自百度百科。

        看官網簡介吧:http://hessian.caucho.com/index.xtp

        The Hessian binary web service protocol makes web services usable without requiring a large framework, and without learning yet another alphabet soup of protocols. Because it is a binary protocol, it is well-suited to sending binary data without any need to extend the protocol with attachments.

        此文使用hessian為hessian-4.0.7,源碼及jar包請自行下載。------當然是java版了,咱也不會別的。

        開搞吧!

        除了提供下載,Hessian官網上貌似就下面幾句話有用了(可能是Hessian使用起來很簡單的原因吧)。

        開搞吧,兩個應用,Client(hessianclient)調用Server(hessianserver)提供的服務並展示返回內容。

        創建公共接口(單獨打包成Jar,方便多個項目使用)。

package com.skyjoo.hessiancommon.interfaces; public interface HessianExampleInterface { public String hello(String name); }

        就一個hello方法,返回String......

        在Server中提供該接口的實現。

package com.skyjoo.hessianserver.interfacesImpl; import com.skyjoo.hessiancommon.interfaces.HessianExampleInterface; public class HessianExampleInterfaceImpl implements HessianExampleInterface { @Override public String hello(String name) { return "Hello " + name + ",Welcome to Hessian World!!!"; } }

        像上面這樣肯定是沒法工作的,只要有WebService經驗或了解過Axis的類似開源框架的童鞋肯定都知道關鍵的是發布服務......

        介紹中的"輕量級"說明發布服務應該是很簡單的......

        本文結合Spring,看一下配置吧。

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="order" value="100" />
        <property name="urlMap">
            <map>
                <entry key="/remote/hessianService.htm" value-ref="exampleService" />
            </map>
        </property>
</bean>
<bean id="exampleService" class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="hessianExampleService" />
        <property name="serviceInterface" value="com.skyjoo.hessiancommon.interfaces.HessianExampleInterface" />
</bean>
<bean id="hessianExampleService" class="com.skyjoo.hessianserver.interfacesImpl.HessianExampleInterfaceImpl" />

    

    <entry key="/remote/hessianService.htm" value-ref="exampleService" />
    將某個地址對應到Bean上,這里的Bean為org.springframework.remoting.caucho.HessianServiceExporter。該Bean的service屬性即服務端對公共接口的實現,serviceInterface即公共接口。還是很容易就能理解的。
接着就是Client的內容。
    <bean id="hessianService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <property name="serviceUrl" value="http://localhost:8090/hessianserver/remote/hessianService.htm" />
        <property name="serviceInterface" value="com.skyjoo.hessiancommon.interfaces.HessianExampleInterface" />
    </bean>

        使用org.springframework.remoting.caucho.HessianProxyFactoryBean,serviceUrl為服務地址,serviceInterface為公共接口。看serviceUrl的內容

    http://localhost:8090/hessianserver/remote/hessianService.htm

前半部分是服務部署地址,后半部分為<entry key="/remote/hessianService.htm" value-ref="exampleService"/>配置中指定的key。

然后......然后就沒有了,Spring怎么裝配Bean的,平時怎么使用Bean的,就怎么玩吧。
測試內容
@Controller public class Test { @Autowired private HessianExampleInterface hessianService; @RequestMapping("/hello") public void hello(@RequestParam("username") String username, ModelMap model) { model.put("helloUser", hessianService.hello(username)); } }

        和使用普通的Bean一模一樣。

(這圖上的好無聊~~~~~~~~~~~)

 只是簡單的實現了一個例子,還沒能深入的學習、理解hessian的內容。
--------------------------------------------------------------------華麗麗的分割線---------------------------------------------------------------

未完待續......

昨天只是簡單的例子,現在續上......
Hessian在Spring中的工作流程如下:
(1)客戶端:
a.發送遠程請求:
客戶端程序-->調用公共接口的方法-->Hessian攔截器攔截請求-->封裝遠程調用請求-->Hessian代理-->通過HTTP協議發送遠程請求代理到服務端
b.接收遠程調用響應:
遠程調用結果-->HTTP響應-->客戶端
(2)服務端:
a.接收遠程調用請求:
HessianServiceExporter接收請求-->將遠程調用對象封裝為HessianSkeleton框架-->
HessianSkeleton處理遠程調用請求
b.返回遠程調用響應:
HessianSkeleton封裝遠程調用處理結果-->HTTP響應-->客戶端
下圖是通過hessian一次完成調用的示意圖


在上面的例子中,Spring通過HessianProxyFactoryBean管理Hessian客戶端。
HessianProxyFactoryBean.java
public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
    //遠程調用代理對象
    private Object serviceProxy;
    //Spring IoC依賴注入完成后的回調方法
    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        //設置遠程代理對象,getServiceInterface()即獲取配置中的serviceInterface(公共接口)
        //HessianProxyFactoryBean 繼HessianClientInterceptor ,本身就是一個攔截器,所以傳入攔截器為this
        this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
    }

    //返回遠程調用代理對象
    public Object getObject() {
        return this.serviceProxy;
    }
    //獲取對象類型(即公共接口)
    public Class<?> getObjectType() {
        return getServiceInterface();
    }
    //判斷是否是單例,Spring默認管理對象都是單例的
    public boolean isSingleton() {
        return true;
    }

}
    HessianProxyFactoryBean最核心的功能是在IoC容器回調方法中產生遠程調用代理對象,將代理對象的攔截器設置為HessianClientInterceptor。
  1 public class HessianClientInterceptor extends UrlBasedRemoteAccessor implements MethodInterceptor {  
  2     //創建Hessiann代理工廠  
  3     private HessianProxyFactory proxyFactory = new HessianProxyFactory();  
  4     //Hessian代理  
  5     private Object hessianProxy;  
  6     //設置Hessian代理工廠  
  7     public void setProxyFactory(HessianProxyFactory proxyFactory) {  
  8         this.proxyFactory = (proxyFactory != null ? proxyFactory : new HessianProxyFactory());  
  9     }  
 10     //設置Hessian序列化工廠  
 11     public void setSerializerFactory(SerializerFactory serializerFactory) {  
 12         this.proxyFactory.setSerializerFactory(serializerFactory);  
 13     }  
 14     //設置Hessian是否發送java集合類型對象  
 15     public void setSendCollectionType(boolean sendCollectionType) {  
 16     this.proxyFactory.getSerializerFactory().setSendCollectionType(sendCollectionType);  
 17     }  
 18     //設置遠程調用時是否重載方法  
 19     public void setOverloadEnabled(boolean overloadEnabled) {  
 20         this.proxyFactory.setOverloadEnabled(overloadEnabled);  
 21     }  
 22     //設置遠程調用用戶名  
 23     public void setUsername(String username) {  
 24         this.proxyFactory.setUser(username);  
 25     }  
 26     //設置遠程調用密碼  
 27     public void setPassword(String password) {  
 28         this.proxyFactory.setPassword(password);  
 29     }  
 30     //設置是否使用Hessian的Debug調試模式  
 31     public void setDebug(boolean debug) {  
 32         this.proxyFactory.setDebug(debug);  
 33     }  
 34     //設置是否使用chunked端口發送Hessian請求  
 35     public void setChunkedPost(boolean chunkedPost) {  
 36         this.proxyFactory.setChunkedPost(chunkedPost);  
 37     }  
 38     //設置Hessian等待響應的超時時長  
 39     public void setReadTimeout(long timeout) {  
 40         this.proxyFactory.setReadTimeout(timeout);  
 41     }  
 42     //設置是否使用Hessain版本2協議解析請求和響應  
 43     public void setHessian2(boolean hessian2) {  
 44         this.proxyFactory.setHessian2Request(hessian2);  
 45         this.proxyFactory.setHessian2Reply(hessian2);  
 46     }  
 47     //設置是否使用Hessian版本2協議解析請求  
 48     public void setHessian2Request(boolean hessian2) {  
 49         this.proxyFactory.setHessian2Request(hessian2);  
 50     }  
 51     //設置是否使用Hessian版本2協議解析響應  
 52     public void setHessian2Reply(boolean hessian2) {  
 53         this.proxyFactory.setHessian2Reply(hessian2);  
 54     }  
 55 //子類HessianProxyFactoryBean的回調方法調用此回調方法  
 56     public void afterPropertiesSet() {  
 57         //調用其父類UrlBasedRemoteAccessor的回調方法獲取客戶端配置的請求url  
 58         super.afterPropertiesSet();  
 59         //初始化Hessian代理  
 60         prepare();  
 61     }  
 62     //初始化Hessian代理  
 63     public void prepare() throws RemoteLookupFailureException {  
 64         try {  
 65             //創建Hessian代理  
 66             this.hessianProxy = createHessianProxy(this.proxyFactory);  
 67         }  
 68         catch (MalformedURLException ex) {  
 69             throw new RemoteLookupFailureException("Service URL [" + getServiceUrl() + "] is invalid", ex);  
 70         }  
 71     }  
 72     //創建Hessian代理  
 73     protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {  
 74         Assert.notNull(getServiceInterface(), "'serviceInterface' is required");  
 75         //使用Hessian代理工廠創建Hessian代理  
 76         return proxyFactory.create(getServiceInterface(), getServiceUrl());  
 77     }  
 78     //攔截器客戶端請求的方法  
 79     public Object invoke(MethodInvocation invocation) throws Throwable {  
 80         if (this.hessianProxy == null) {  
 81             throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +  
 82                     "invoke 'prepare' before attempting any operations");  
 83         }  
 84         //獲取當前環境中線程類加載器  
 85         ClassLoader originalClassLoader = overrideThreadContextClassLoader();  
 86         try {  
 87         //調用Hessian代理的方法,是Hessian遠程調用的入口方法,使用JDK反射機制  
 88             return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());  
 89         }  
 90         //處理Hessian遠程調用中的異常  
 91         catch (InvocationTargetException ex) {  
 92             Throwable targetEx = ex.getTargetException();  
 93             if (targetEx instanceof InvocationTargetException) {  
 94                 targetEx = ((InvocationTargetException) targetEx).getTargetException();  
 95             }  
 96             if (targetEx instanceof HessianConnectionException) {  
 97                 throw convertHessianAccessException(targetEx);  
 98             }  
 99             else if (targetEx instanceof HessianException || targetEx instanceof HessianRuntimeException) {  
100                 Throwable cause = targetEx.getCause();  
101                 throw convertHessianAccessException(cause != null ? cause : targetEx);  
102             }  
103             else if (targetEx instanceof UndeclaredThrowableException) {  
104                 UndeclaredThrowableException utex = (UndeclaredThrowableException) targetEx;  
105                 throw convertHessianAccessException(utex.getUndeclaredThrowable());  
106             }  
107             else {  
108                 throw targetEx;  
109             }  
110         }  
111         catch (Throwable ex) {  
112             throw new RemoteProxyFailureException(  
113                     "Failed to invoke Hessian proxy for remote service [" + getServiceUrl() + "]", ex);  
114         }  
115         //重置類加載器  
116         finally {  
117             resetThreadContextClassLoader(originalClassLoader);  
118         }  
119     }  
120     //將Hessian異常轉換為Spring遠程調用異常  
121     protected RemoteAccessException convertHessianAccessException(Throwable ex) {  
122         if (ex instanceof HessianConnectionException || ex instanceof ConnectException) {  
123             return new RemoteConnectFailureException(  
124                     "Cannot connect to Hessian remote service at [" + getServiceUrl() + "]", ex);  
125         }  
126         else {  
127             return new RemoteAccessException(  
128                 "Cannot access Hessian remote service at [" + getServiceUrl() + "]", ex);  
129         }  
130     }  
131 }  
    上面源碼中最重要的方法是對遠程調用攔截的方法invoke,在該方法中使用JDK的反射機制調用Hessian代理對象的指定方法。而Hessian代理是由Hessain代理器工廠HessianProxyFactory產生的,這個Hessian代理器工廠是有Hessian提供的。
接着看Hessian的具體實現,這塊已經和Spring沒有關系了。
HessianProxy是hessian client處理客戶端請求的核心類,采用proxy模式,代理客戶端對遠程接口的調用,hessian client的主程序的時序圖如下:

    HessianProxy在invoke方法中實現對調用方法數參數的序列化為預定格式的輸出流,調用sendRequest發送請求,獲取返回輸入流。
HessianProxy.java
  1 public Object invoke(Object proxy, Method method, Object []args)
  2     throws Throwable
  3   {
  4     String mangleName;
  5 
  6     synchronized (_mangleMap) {
  7       mangleName = _mangleMap.get(method);
  8     }
  9 
 10     if (mangleName == null) {
 11       String methodName = method.getName();
 12       Class<?> []params = method.getParameterTypes();
 13 
 14       // equals and hashCode are special cased
 15       if (methodName.equals("equals")
 16       && params.length == 1 && params[0].equals(Object.class)) {
 17     Object value = args[0];
 18     if (value == null || ! Proxy.isProxyClass(value.getClass()))
 19       return Boolean.FALSE;
 20 
 21     Object proxyHandler = Proxy.getInvocationHandler(value);
 22 
 23     if (! (proxyHandler instanceof HessianProxy))
 24       return Boolean.FALSE;
 25     
 26     HessianProxy handler = (HessianProxy) proxyHandler;
 27 
 28     return new Boolean(_url.equals(handler.getURL()));
 29       }
 30       else if (methodName.equals("hashCode") && params.length == 0)
 31     return new Integer(_url.hashCode());
 32       else if (methodName.equals("getHessianType"))
 33     return proxy.getClass().getInterfaces()[0].getName();
 34       else if (methodName.equals("getHessianURL"))
 35     return _url.toString();
 36       else if (methodName.equals("toString") && params.length == 0)
 37     return "HessianProxy[" + _url + "]";
 38       
 39       if (! _factory.isOverloadEnabled())
 40     mangleName = method.getName();
 41       else
 42         mangleName = mangleName(method);
 43 
 44       synchronized (_mangleMap) {
 45     _mangleMap.put(method, mangleName);
 46       }
 47     }
 48 
 49     InputStream is = null;
 50     HessianConnection conn = null;
 51     
 52     try {
 53       if (log.isLoggable(Level.FINER))
 54     log.finer("Hessian[" + _url + "] calling " + mangleName);
 55       
 56       conn = sendRequest(mangleName, args);
 57 
 58       is = conn.getInputStream();
 59 
 60       if (log.isLoggable(Level.FINEST)) {
 61     PrintWriter dbg = new PrintWriter(new LogWriter(log));
 62     HessianDebugInputStream dIs
 63       = new HessianDebugInputStream(is, dbg);
 64 
 65     dIs.startTop2();
 66     
 67     is = dIs;
 68       }
 69 
 70       AbstractHessianInput in;
 71 
 72       int code = is.read();
 73 
 74       if (code == 'H') {
 75     int major = is.read();
 76     int minor = is.read();
 77 
 78     in = _factory.getHessian2Input(is);
 79 
 80     Object value = in.readReply(method.getReturnType());
 81 
 82     return value;
 83       }
 84       else if (code == 'r') {
 85     int major = is.read();
 86     int minor = is.read();
 87     
 88     in = _factory.getHessianInput(is);
 89 
 90     in.startReplyBody();
 91 
 92     Object value = in.readObject(method.getReturnType());
 93 
 94     if (value instanceof InputStream) {
 95       value = new ResultInputStream(conn, is, in, (InputStream) value);
 96       is = null;
 97       conn = null;
 98     }
 99     else
100       in.completeReply();
101 
102     return value;
103       }
104       else
105     throw new HessianProtocolException("'" + (char) code + "' is an unknown code");
106     } catch (HessianProtocolException e) {
107       throw new HessianRuntimeException(e);
108     } finally {
109       try {
110     if (is != null)
111       is.close();
112       } catch (Exception e) {
113     log.log(Level.FINE, e.toString(), e);
114       }
115       
116       try {
117     if (conn != null)
118       conn.destroy();
119       } catch (Exception e) {
120     log.log(Level.FINE, e.toString(), e);
121       }
122     }
123   }
    1~47行主要是把methodName緩存起來和過濾一些特殊調用,java反射是個比較耗性能的操作,把methodName緩存起來可以避免每次調用都要從method里得到methodName。另外,對equals、hashCode、getHessianType、getHessianURL等特殊方法的遠程調用,直接在本地處理。56行調用了sendRequest方法,在sendRequest方法中初始化連接,調用AbstractHessianOutput包裝網絡輸出流,通過AbstractHessianOutput.call(methodName, args)完成網絡輸出。
到這里client先告一段落吧,網絡怎么輸出的等等沒去看......


Server端通過HessianServiceExporter處理遠程調用請求。HessianServiceExporter實現了HttpRequestHandler接口,通過handleRequest方法處理請求。
 1 public class HessianServiceExporter extends HessianExporter implements HttpRequestHandler {
 2 
 3     /**
 4      * Processes the incoming Hessian request and creates a Hessian response.
 5      */
 6     public void handleRequest(HttpServletRequest request, HttpServletResponse response)
 7             throws ServletException, IOException {
 8 
 9         if (!"POST".equals(request.getMethod())) {
10             throw new HttpRequestMethodNotSupportedException(request.getMethod(),
11                     new String[] {"POST"}, "HessianServiceExporter only supports POST requests");
12         }
13 
14         response.setContentType(CONTENT_TYPE_HESSIAN);
15         try {
16           invoke(request.getInputStream(), response.getOutputStream());
17         }
18         catch (Throwable ex) {
19           throw new NestedServletException("Hessian skeleton invocation failed", ex);
20         }
21     }
22 
23 }
    HessianServiceExporter繼承了HessianExporter,調用父類的invoke處理輸入流。在HessianExporter的invoke中調用了HessianSkeleton.invoke(InputStream is, OutputStream os)。
HessianSkeleton是Hessian server端的核心類,主要功能是接收網絡輸入流(被包裝為AbstractHessianInput),反序列化輸入流得到methodName和參數,然后根據配置的service,通過反射調用實現類的方法,得到結果后序列化為輸出流返回給客戶端,流程如下:

    
    HessianSkeleton核心的反射調用非常清晰
 1 public void invoke(Object service,
 2                      AbstractHessianInput in,
 3                      AbstractHessianOutput out)
 4     throws Exception
 5   {
 6     ServiceContext context = ServiceContext.getContext();
 7 
 8     // backward compatibility for some frameworks that don't read
 9     // the call type first
10     in.skipOptionalCall();
11 
12     // Hessian 1.0 backward compatibility
13     String header;
14     while ((header = in.readHeader()) != null) {
15       Object value = in.readObject();
16 
17       context.addHeader(header, value);
18     }
19 
20     String methodName = in.readMethod();
21     int argLength = in.readMethodArgLength();
22 
23     Method method;
24 
25     method = getMethod(methodName + "__" + argLength);
26 
27     if (method == null)
28       method = getMethod(methodName);
29 
30     if (method != null) {
31     }
32     else if ("_hessian_getAttribute".equals(methodName)) {
33       String attrName = in.readString();
34       in.completeCall();
35 
36       String value = null;
37 
38       if ("java.api.class".equals(attrName))
39         value = getAPIClassName();
40       else if ("java.home.class".equals(attrName))
41         value = getHomeClassName();
42       else if ("java.object.class".equals(attrName))
43         value = getObjectClassName();
44 
45       out.writeReply(value);
46       out.close();
47       return;
48     }
49     else if (method == null) {
50       out.writeFault("NoSuchMethodException",
51                      "The service has no method named: " + in.getMethod(),
52                      null);
53       out.close();
54       return;
55     }
56 
57     Class<?> []args = method.getParameterTypes();
58 
59     if (argLength != args.length && argLength >= 0) {
60       out.writeFault("NoSuchMethod",
61                      "method " + method + " argument length mismatch, received length=" + argLength,
62                      null);
63       out.close();
64       return;
65     }
66 
67     Object []values = new Object[args.length];
68 
69     for (int i = 0; i < args.length; i++) {
70       // XXX: needs Marshal object
71       values[i] = in.readObject(args[i]);
72     }
73 
74     Object result = null;
75 
76     try {
77       result = method.invoke(service, values);
78     } catch (Exception e) {
79       Throwable e1 = e;
80       if (e1 instanceof InvocationTargetException)
81         e1 = ((InvocationTargetException) e).getTargetException();
82 
83       log.log(Level.FINE, this + " " + e1.toString(), e1);
84 
85       out.writeFault("ServiceException", e1.getMessage(), e1);
86       out.close();
87       return;
88     }
89 
90     // The complete call needs to be after the invoke to handle a
91     // trailing InputStream
92     in.completeCall();
93 
94     out.writeReply(result);
95 
96     out.close();
97   }
    關鍵在於輸入輸出流的序列化,這部分內容暫未了解......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM