Hessian解析及应用(整合Spring)
来源:互联网 发布:qq电脑管家网络加速器 编辑:程序博客网 时间:2024/06/01 07:56
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的,就怎么玩吧。
测试内容
@Controllerpublic 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 else100 in.completeReply();101 102 return value;103 }104 else105 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 first10 in.skipOptionalCall();11 12 // Hessian 1.0 backward compatibility13 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 object71 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 a91 // trailing InputStream92 in.completeCall();93 94 out.writeReply(result);95 96 out.close();97 }
关键在于输入输出流的序列化,这部分内容暂未了解......
- Hessian解析及应用(整合Spring)
- Hessian解析及应用
- Hessian(1)Spring整合Hessian
- spring boot整合hessian(十二)
- Spring整合Hessian教程
- Spring 整合 Hessian
- Hessian和Spring整合
- Spring整合Hessian
- Spring整合Hessian
- Spring 整合 Hessian
- Spring整合Hessian
- Spring整合Hessian服务
- Spring整合Hessian
- hessian spring实例整合
- spring boot整合hessian
- Spring整合hessian和burlap及自带的Invoker
- spring hessian应用
- spring整合hessian学习笔记
- java selenium (二) 环境搭建方法一
- spring boot中关于redis 保存数据的序列化(数据库中的乱码问题)
- Qt 应用获取本机设备IP
- 【脚本语言系列】关于Python数值计算Pandas,你需要知道的事
- 使用Hibernate Criteria实现in子查询和exists子查询
- Hessian解析及应用(整合Spring)
- css3深入理解之border-radius
- java selenium (三) 环境搭建 基于Maven
- 时间复杂度:O(1)、O(n)、O(n²)、O(nlogn)等是什么意思,白话文解释专业术语。
- hive安装
- Android图片轮播控件ConvenientBanner的简单使用
- 从零开始学Makefile(四)
- JS三元运算符
- 《统计学习方法》学习笔记(5)-- 朴素贝叶斯法