Hessian source code analysis and Hack -- let Hessian carry the information of the remote caller (below)

Next, let's talk about how the Spring-packaged Hessian comes to Hack. 
At first, I thought that as long as I changed the source code of hessian, it would be fine. In fact, it is not, because Spring has passed several layers of packaging, so that you cannot get the request object. 
The entry of hessian in Spring is the class HessianServiceExporter. There are two main methods. 

Java code   Favorite code
  1. public void prepare() {  
  2.     HessianSkeleton skeleton = null;  
  3.   
  4.     try {  
  5.         try {  
  6.             // Try Hessian 3.x (with service interface argument).  
  7.             Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class, Class.class});  
  8.             checkService();  
  9.             checkServiceInterface();  
  10.             skeleton = (HessianSkeleton)  
  11.                     ctor.newInstance(new Object[] {getProxyForService(), getServiceInterface()});  
  12.         }  
  13.         catch (NoSuchMethodException ex) {  
  14.             // Fall back to Hessian 2.x (without service interface argument).  
  15.             Constructor ctor = HessianSkeleton.class.getConstructor(new Class[] {Object.class});  
  16.             skeleton = (HessianSkeleton) ctor.newInstance(new Object[] {getProxyForService()});  
  17.         }  
  18.     }  
  19.     catch (Throwable ex) {  
  20.         throw new BeanInitializationException("Hessian skeleton initialization failed", ex);  
  21.     }  
  22.   
  23.     if (hessian2Available) {  
  24.         // Hessian 2 (version 3.0.20+).  
  25.         this.skeletonInvoker = new Hessian2SkeletonInvoker(skeleton, this.serializerFactory);  
  26.     }  
  27.     else {  
  28.         // Hessian 1 (version 3.0.19-).  
  29.         this.skeletonInvoker = new Hessian1SkeletonInvoker(skeleton, this.serializerFactory);  
  30.     }  
  31. }  
  32.   
  33.   
  34. /** 
  35.  * Processes the incoming Hessian request and creates a Hessian response. 
  36.  */  
  37. public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
  38.         throws ServletException, IOException {  
  39.   
  40.     Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");  
  41.             if (!"POST".equals(request.getMethod())) {  
  42.         throw new HttpRequestMethodNotSupportedException(  
  43.                 "POST""HessianServiceExporter only supports POST requests");  
  44.     }  
  45.   
  46.     try {  
  47.       this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  
  48.     }  
  49.     catch (Throwable ex) {  
  50.       throw new NestedServletException("Hessian skeleton invocation failed", ex);  
  51.     }finally{  
  52.         ServiceContext.end();  
  53.     }  
  54. }  



prepare()方法是在Spring环境加载的时候执行的,用来初始化Hessian的HessianSkeleton并包装成Spring自己的Hessian2SkeletonInvoker。 
handleRequest()方法是每次远程调用的入口,里边的这个方法执行后续hessian的解析流程 

Java代码   Favorite code
  1. this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  


skeletonInvoker也就是prepare里初始化的Hessian2SkeletonInvoker,跟进去看看先 

Java代码   Favorite code
  1. class Hessian2SkeletonInvoker extends HessianSkeletonInvoker {  
  2.   
  3.     public Hessian2SkeletonInvoker(HessianSkeleton skeleton, SerializerFactory serializerFactory) {  
  4.         super(skeleton, serializerFactory);  
  5.     }  
  6.   
  7.     public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {  
  8.         Hessian2Input in = new Hessian2Input(inputStream);  
  9.         if (this.serializerFactory != null) {  
  10.             in.setSerializerFactory(this.serializerFactory);  
  11.         }  
  12.   
  13.         int code = in.read();  
  14.         if (code != 'c') {  
  15.             throw new IOException("expected 'c' in hessian input at " + code);  
  16.         }  
  17.   
  18.         AbstractHessianOutput out = null;  
  19.         int major = in.read();  
  20.         int minor = in.read();  
  21.         if (major >= 2) {  
  22.             out = new Hessian2Output(outputStream);  
  23.         }  
  24.         else {  
  25.             out = new HessianOutput(outputStream);  
  26.         }  
  27.         if (this.serializerFactory != null) {  
  28.             out.setSerializerFactory(this.serializerFactory);  
  29.         }  
  30.   
  31.         this.skeleton.invoke(in, out);  
  32.     }  
  33.   
  34. }  


Hessian2SkeletonInvoker的构造函数进行父类的初始化,也就是初始化了最重要的HessianSkeleton 
再看invoke()方法,是不是有点眼熟,对了,这就是hessian源代码里边的以部分代码,用来包装输入输入流为Hessian自己的Hessian2Input 等,然后判断开头的字节是否正确,以及确定HessianOutput的版本。这些都是解析输入流里边的字节标志位来实现的。 在方法的最后调用了

Java代码   Favorite code
  1. this.skeleton.invoke(in, out);  

,也就是把控制权交给了Hessian来处理,这样后续的工作就是Hessian中完成。但是这样就ok了吗?慢着,我们是不是漏了点什么? 对,那个ServiceContext上下文。因为Hessian是在自己的入口Servlet中生成上下文的,但是Spring并没有生成这个东西。所以,我在这里自己生成了它,唯一一个能够生成的地方就在Spring的HessianServiceExporter的handleRequest()里边,只有在这里才有request对象。因此我把handleRequest()方法中加了一句ServiceContext.begin(request, null, null);然后在finally里边加了一句ServiceContext.end(); 
代码如下: 

Java代码   Favorite code
  1. public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
  2.         throws ServletException, IOException {  
  3.   
  4.     Assert.notNull(this.skeletonInvoker, "HessianServiceExporter has not been initialized");  
  5.     ServiceContext.begin(request, nullnull);  
  6.     if (!"POST".equals(request.getMethod())) {  
  7.         throw new HttpRequestMethodNotSupportedException(  
  8.                 "POST""HessianServiceExporter only supports POST requests");  
  9.     }  
  10.   
  11.     try {  
  12.       this.skeletonInvoker.invoke(request.getInputStream(), response.getOutputStream());  
  13.     }  
  14.     catch (Throwable ex) {  
  15.       throw new NestedServletException("Hessian skeleton invocation failed", ex);  
  16.     }finally{  
  17.         ServiceContext.end();  
  18.     }  
  19. }  



The ServiceContext.begin() method generates a ThreadLocal object, and ServiceContext.end() releases the context in it. 
After adding this place, you can use the above hessian source code for the remaining operations 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327103126&siteId=291194637