Hessian源码分析--HessianProxy
在上一篇博客 Hessian源码分析--HessianProxyFactory 中我们了解到,客户端获得的对象其实是HessianProxy生成的目标对象,当调用目标对象的方法时,会调用HessianProxy的invoke方法,如下,当调用HelloService的helloWorld函数时,会调用HessianProxy的invoke函数(对代理机制不懂的同学可以去学习一下)。
HessianProxyFactory factory = new HessianProxyFactory(); HelloService helloService = (HelloService) factory.create(HelloService.class, url); System.out.println(helloService.helloWorld("world"));
接下来我们一步一步分析invoke函数,就会对Hessian的机制有一个比较清楚的了解:
invoke方法参数如下
/** * Handles the object invocation. * * @param proxy 就是HessianProxy * @param method 调用的方法 * @param args 方法需要的参数 */ public Object invoke(Object proxy, Method method, Object []args) throws Throwable
String mangleName; //方法对象对应的名称缓存起来 synchronized (_mangleMap) { mangleName = _mangleMap.get(method); } if (mangleName == null) { //获得方法名称 String methodName = method.getName(); //方法参数对象类型 Class<?> []params = method.getParameterTypes(); // equals and hashCode are special cased //如果调用的方法是equals或者hashCode就特殊处理,不用远程调用 if (methodName.equals("equals") && params.length == 1 && params[0].equals(Object.class)) { Object value = args[0]; if (value == null || ! Proxy.isProxyClass(value.getClass())) return Boolean.FALSE; Object proxyHandler = Proxy.getInvocationHandler(value); if (! (proxyHandler instanceof HessianProxy)) return Boolean.FALSE; HessianProxy handler = (HessianProxy) proxyHandler; return new Boolean(_url.equals(handler.getURL())); } else if (methodName.equals("hashCode") && params.length == 0) return new Integer(_url.hashCode()); else if (methodName.equals("getHessianType")) return proxy.getClass().getInterfaces()[0].getName(); else if (methodName.equals("getHessianURL")) return _url.toString(); else if (methodName.equals("toString") && params.length == 0) return "HessianProxy[" + _url + "]"; if (! _factory.isOverloadEnabled()) mangleName = method.getName(); else mangleName = mangleName(method); //保存函数对象对应的函数名称 synchronized (_mangleMap) { _mangleMap.put(method, mangleName); } }
这部分操作是Hessian的关键,sendRequest就是向服务端发送请求,这期间还包括对参数值的序列化,发送请求之后,服务端会根据参数来调用服务端函数并将调用结果序列化之后返回,这样就可以通过conn.getInputStream来获得返回结果,
InputStream is = null; HessianConnection conn = null; try { if (log.isLoggable(Level.FINER)) log.finer("Hessian[" + _url + "] calling " + mangleName); //向server发送请求,包括函数名及函数的参数 conn = sendRequest(mangleName, args); //获取请求调用之后的返回值 is = getInputStream(conn); if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugInputStream dIs = new HessianDebugInputStream(is, dbg); dIs.startTop2(); is = dIs; }
上面我们大体分析了一下sendRequest函数,接下来我们详细介绍一下这个函数的具体操作:
首先会根据URL来通过获得conn连接对象,接下来通过addRequestHeaders来设置一些默认的头部信息,
通过conn.getOutputStream()来获取OutputStream对象,根据OutputStream对象接下来获得Hessian提供的序列化对象out,
调用out.call(methodName, args)来序列化需要远程调用的函数名称和参数值,out.flush();将传递的数据刷新到outputStream中,
最后通过调用 conn.sendRequest();来发送请求,这个时候服务暴露端会得到这个请求(具体请求执行我们接下来分析)
并返回conn,此时调用结果值应该在conn的InputStream中了。
protected HessianConnection sendRequest(String methodName, Object []args) throws IOException { HessianConnection conn = null; //根据URL获取链接对象 conn = _factory.getConnectionFactory().open(_url); boolean isValid = false; try { //在请求中设置一些头的默认值 addRequestHeaders(conn); OutputStream os = null; try { //获取OutputStream os = conn.getOutputStream(); } catch (Exception e) { throw new HessianRuntimeException(e); } if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg); dOs.startTop2(); os = dOs; } AbstractHessianOutput out = _factory.getHessianOutput(os); //对函数名和参数值进行二进制序列化 out.call(methodName, args); out.flush(); //向暴露的服务端发送请求 conn.sendRequest(); isValid = true; return conn; } finally { if (! isValid && conn != null) conn.destroy(); } }
当服务暴露端返回值之后,接下来就是返回值的处理了,因为服务端给我们的也是二进制数据,所以我们需要感觉函数参数的类型来反序列化得到结果,上面我们已经分析到
is = getInputStream(conn),这样返回值就在is中,首先第一个读到的是一个code,这样应该是一个标记值,接下来读到的就是主版本号和副版本号了,
in = _factory.getHessian2Input(is)是用来获得反序列化对象
Object value = in.readReply(method.getReturnType()) 这样就可以根据方法的返回类型来反序列化结果值了,这样整个的Hessian的客户端的实现机制就是这样了。
AbstractHessianInput in; int code = is.read(); if (code == 'H') { int major = is.read(); int minor = is.read(); in = _factory.getHessian2Input(is); Object value = in.readReply(method.getReturnType()); return value; } else if (code == 'r') { int major = is.read(); int minor = is.read(); in = _factory.getHessianInput(is); in.startReplyBody(); Object value = in.readObject(method.getReturnType()); if (value instanceof InputStream) { value = new ResultInputStream(conn, is, in, (InputStream) value); is = null; conn = null; } else in.completeReply(); return value;
接下来我们会对序列化和反序列化机制,已经连接请求机制也会简单分析。
HessianProxy的完整源码解析:
public class HessianProxy implements InvocationHandler, Serializable { private static final Logger log = Logger.getLogger(HessianProxy.class.getName()); protected HessianProxyFactory _factory; private WeakHashMap<Method,String> _mangleMap = new WeakHashMap<Method,String>(); private Class<?> _type; private URL _url; protected HessianProxy(URL url, HessianProxyFactory factory) { this(url, factory, null); } protected HessianProxy(URL url, HessianProxyFactory factory, Class<?> type) { _factory = factory; _url = url; _type = type; } public URL getURL() { return _url; } /** * Handles the object invocation. * * @param proxy 就是HessianProxy * @param method 调用的方法 * @param args 方法需要的参数 */ public Object invoke(Object proxy, Method method, Object []args) throws Throwable { String mangleName; //方法对象对应的名称缓存起来 synchronized (_mangleMap) { mangleName = _mangleMap.get(method); } if (mangleName == null) { //获得方法名称 String methodName = method.getName(); //方法参数对象类型 Class<?> []params = method.getParameterTypes(); // equals and hashCode are special cased //如果调用的方法是equals或者hashCode就特殊处理,不用远程调用 if (methodName.equals("equals") && params.length == 1 && params[0].equals(Object.class)) { Object value = args[0]; if (value == null || ! Proxy.isProxyClass(value.getClass())) return Boolean.FALSE; Object proxyHandler = Proxy.getInvocationHandler(value); if (! (proxyHandler instanceof HessianProxy)) return Boolean.FALSE; HessianProxy handler = (HessianProxy) proxyHandler; return new Boolean(_url.equals(handler.getURL())); } else if (methodName.equals("hashCode") && params.length == 0) return new Integer(_url.hashCode()); else if (methodName.equals("getHessianType")) return proxy.getClass().getInterfaces()[0].getName(); else if (methodName.equals("getHessianURL")) return _url.toString(); else if (methodName.equals("toString") && params.length == 0) return "HessianProxy[" + _url + "]"; if (! _factory.isOverloadEnabled()) mangleName = method.getName(); else mangleName = mangleName(method); //保存函数对象对应的函数名称 synchronized (_mangleMap) { _mangleMap.put(method, mangleName); } } InputStream is = null; HessianConnection conn = null; try { if (log.isLoggable(Level.FINER)) log.finer("Hessian[" + _url + "] calling " + mangleName); //向server发送请求,包括函数名及函数的参数 conn = sendRequest(mangleName, args); //获取请求调用之后的返回值 is = getInputStream(conn); if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugInputStream dIs = new HessianDebugInputStream(is, dbg); dIs.startTop2(); is = dIs; } AbstractHessianInput in; //获取返回码 int code = is.read(); if (code == 'H') { int major = is.read(); int minor = is.read(); //获得对象Hessian2Input in = _factory.getHessian2Input(is); //根据方法返回值类型,反序列化获得返回值 Object value = in.readReply(method.getReturnType()); return value; } else if (code == 'r') { int major = is.read(); int minor = is.read(); in = _factory.getHessianInput(is); in.startReplyBody(); Object value = in.readObject(method.getReturnType()); if (value instanceof InputStream) { value = new ResultInputStream(conn, is, in, (InputStream) value); is = null; conn = null; } else in.completeReply(); //得到远程调用结果 return value; } else throw new HessianProtocolException("'" + (char) code + "' is an unknown code"); } catch (HessianProtocolException e) { throw new HessianRuntimeException(e); } finally { try { if (is != null) is.close(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } try { if (conn != null) conn.destroy(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } } //获取HessianConnection中的返回值 protected InputStream getInputStream(HessianConnection conn) throws IOException { InputStream is = conn.getInputStream(); if ("deflate".equals(conn.getContentEncoding())) { is = new InflaterInputStream(is, new Inflater(true)); } return is; } protected String mangleName(Method method) { Class<?> []param = method.getParameterTypes(); if (param == null || param.length == 0) return method.getName(); else return AbstractSkeleton.mangleName(method, false); } /** * Sends the HTTP request to the Hessian connection. */ protected HessianConnection sendRequest(String methodName, Object []args) throws IOException { HessianConnection conn = null; //根据URL获取链接对象 conn = _factory.getConnectionFactory().open(_url); boolean isValid = false; try { //在请求中设置一些头的默认值 addRequestHeaders(conn); OutputStream os = null; try { //获取OutputStream os = conn.getOutputStream(); } catch (Exception e) { throw new HessianRuntimeException(e); } if (log.isLoggable(Level.FINEST)) { PrintWriter dbg = new PrintWriter(new LogWriter(log)); HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg); dOs.startTop2(); os = dOs; } AbstractHessianOutput out = _factory.getHessianOutput(os); //对函数名和参数值进行二进制序列化 out.call(methodName, args); out.flush(); //向暴露的服务端发送请求 conn.sendRequest(); isValid = true; return conn; } finally { if (! isValid && conn != null) conn.destroy(); } } //在请求中设置一些头的默认值 protected void addRequestHeaders(HessianConnection conn) { conn.addHeader("Content-Type", "x-application/hessian"); conn.addHeader("Accept-Encoding", "deflate"); String basicAuth = _factory.getBasicAuth(); if (basicAuth != null) conn.addHeader("Authorization", basicAuth); } /** * Method that allows subclasses to parse response headers such as cookies. * Default implementation is empty. * @param conn */ protected void parseResponseHeaders(URLConnection conn) { } public Object writeReplace() { return new HessianRemote(_type.getName(), _url.toString()); } static class ResultInputStream extends InputStream { private HessianConnection _conn; private InputStream _connIs; private AbstractHessianInput _in; private InputStream _hessianIs; ResultInputStream(HessianConnection conn, InputStream is, AbstractHessianInput in, InputStream hessianIs) { _conn = conn; _connIs = is; _in = in; _hessianIs = hessianIs; } public int read() throws IOException { if (_hessianIs != null) { int value = _hessianIs.read(); if (value < 0) close(); return value; } else return -1; } public int read(byte []buffer, int offset, int length) throws IOException { if (_hessianIs != null) { int value = _hessianIs.read(buffer, offset, length); if (value < 0) close(); return value; } else return -1; } public void close() throws IOException { HessianConnection conn = _conn; _conn = null; InputStream connIs = _connIs; _connIs = null; AbstractHessianInput in = _in; _in = null; InputStream hessianIs = _hessianIs; _hessianIs = null; try { if (hessianIs != null) hessianIs.close(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } try { if (in != null) { in.completeReply(); in.close(); } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } try { if (connIs != null) { connIs.close(); } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } try { if (conn != null) { conn.close(); } } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } } } //日志相关 static class LogWriter extends Writer { private Logger _log; private Level _level = Level.FINEST; private StringBuilder _sb = new StringBuilder(); LogWriter(Logger log) { _log = log; } public void write(char ch) { if (ch == '\n' && _sb.length() > 0) { _log.fine(_sb.toString()); _sb.setLength(0); } else _sb.append((char) ch); } public void write(char []buffer, int offset, int length) { for (int i = 0; i < length; i++) { char ch = buffer[offset + i]; if (ch == '\n' && _sb.length() > 0) { _log.log(_level, _sb.toString()); _sb.setLength(0); } else _sb.append((char) ch); } } public void flush() { } public void close() { if (_sb.length() > 0) _log.log(_level, _sb.toString()); } } }
Hessian源码分析--HessianProxy的更多相关文章
- Hessian源码分析--总体架构
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...
- Hessian源码分析--HessianSkeleton
HessianSkeleton是Hessian的服务端的核心,简单总结来说:HessianSkeleton根据客户端请求的链接,获取到需要执行的接口及实现类,对客户端发送过来的二进制数据进行反序列化, ...
- (转)hessian源码分析(一)------架构
在计费中心的对外交互这块采用了hessian,有必要对hessian的运行机理和源码做一定的解析. 大致翻了翻源码后,发现hessian的主要结构分客户端与服务端,中间基于http传输.客户端主要做的 ...
- Hessian源码分析--HessianProxyFactory
HessianProxyFactory是HessianProxy的工厂类,其通过HessianProxy来生成代理类. 如下面代码: HessianProxyFactory factory = new ...
- Hessian源码分析--HessianServlet
Hessian可以通过Servlet来对外暴露服务,HessianServlet继承于HttpServlet,但这仅仅是一个外壳,使用web服务器来提供对外的Http请求,在web.xml中我们会进行 ...
- SURF算法与源码分析、下
上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SU ...
- Dubbo 源码分析 - 服务引用
1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...
- 【OpenCV】SIFT原理与源码分析:关键点搜索与定位
<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了 ...
- 12.源码分析—如何为SOFARPC写一个序列化?
SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...
随机推荐
- 服务器使用nginx做代理,通过HttpServletRequest获取请求用户真实IP地址
首先,在nginx配置中添加如下配置 server { listen ; server_name www.wenki.info; #要访问的域名 charset utf8; location / { ...
- 记录一次widora sdk编译ipk 实战编译redis
因为业务需求,需要用到redis存储一点简单的数据,因为redis有良好的哈希机制,可以完美实现我的某些需求,但openwrt官方提供memcached的ipk并没有提供redis,没办法,只能自 ...
- Recycleview实现复杂布局
Recycleview实现复杂布局 首先 附上Demo链接和效果供大家参考 Demo 实现思路 代码思考 时间是一切财富中最宝贵的财富. -- 德奥弗拉斯多 <a 实现思路 开始看到设计稿子的时 ...
- new File()
首先 File 类是对文件系统的映射 并不是硬盘上真实的文件所以 new File("xxx.xxx") 只是在内存中创建File文件映射对象,而并不会在硬盘中创建文件 如果需要创 ...
- mac下怎么删除隐藏文件比如 .Trashes文件
U盘和移动硬盘接入Mac时会产生.Trashes,.Spotlight-V100,.fseventsd等文件 每插入Mac一次,都会检查是否有这些文件,如果没有,就会创建这些文件 特别是有时候,在文件 ...
- Python:操作数据库
(一) 前言 本文说明如何连接Oracle.MySQL.sqlserver,以及执行sql.获取查询结果等. (二) DB-API DB-API阐明一系列所需对象和数据库 ...
- cassandra 3.x官方文档(3)---gossip通信协议及故障检测与恢复
写在前面 cassandra3.x官方文档的非官方翻译.翻译内容水平全依赖本人英文水平和对cassandra的理解.所以强烈建议阅读英文版cassandra 3.x 官方文档.此文档一半是翻译,一半是 ...
- webpack 1.x 配合npm scripts管理多站点
需求场景: 希望通过一个webpack文件夹管理多个站点的打包流程. 假设现在我要为站点domain配置打包流程. npm 添加淘宝镜像 你懂得 vim ~/.npmrc registry = htt ...
- 负载均衡LVS(DR模式)安装实战
1.编译安装ipvsadm 首先从LVS官网下载tarball,解压后make && make install即可. 要注意的是LVS的依赖有:popt-static.libnl.ke ...
- Android常用的编译命令
1.make -jX X表示数字,这个命令将编译Android系统并生成镜像,XX表示可以使用到的CPU核数,这在配置好的电脑上特别有用,公司的16核ubuntu服务器执行make -j16只要不到2 ...