Hessian源码学习(1)

来源:互联网 发布:知乎led灯化妆镜 编辑:程序博客网 时间:2024/06/02 22:34
HessianServlet 是一个非常普通的Servlet 它直接继承 GenericServlet 

我们看其中两个核心方法:init与service方法(参考源码版本3.0.13) 

init(ServletConfig config)
 

该方法覆写父类的init(ServletConfig config)方法(按照servlet规范推荐,覆写init()方法更好) 

在初始化中,它做了哪些事情呢? 

第一步.初始化远程服务类(Impl) 
Java代码  收藏代码
  1. if (_homeImpl != null) {  
  2.     }  
  3. else if (getInitParameter("home-class") != null) {  
  4.     String className = getInitParameter("home-class");  
  5.   
  6.     Class homeClass = loadClass(className);  
  7.   
  8.     _homeImpl = homeClass.newInstance();  
  9.   
  10.     init(_homeImpl);  
  11.     }  
  12. else if (getInitParameter("service-class") != null) {  
  13.     String className = getInitParameter("service-class");  
  14.   
  15.     Class homeClass = loadClass(className);  
  16.   
  17.     _homeImpl = homeClass.newInstance();  
  18.   
  19.     init(_homeImpl);  
  20. }  
  21. else {  
  22.     if (getClass().equals(HessianServlet.class))  
  23.       throw new ServletException("server must extend HessianServlet");  
  24.   
  25.     _homeImpl = this;  
  26.       }  

其中home-class或者service-class就是在web.xml配置的提供远程服务的类(两者取一个作为配置即可); 

它首先通过getInitParameter方法获取类名,然后调用loadClass获取对应的Class,最后newInstance创建实例; 

若用户未配置,使用当前Servlet作为默认实现类(不能为HessianServlet); 

我们接着往下看:init(_homeImpl) 又做了些什么呢? 
Java代码  收藏代码
  1. private void init(Object service)  
  2.     throws ServletException  
  3. {  
  4.     if (service instanceof Service)  
  5.       ((Service) service).init(getServletConfig());  
  6.     else if (service instanceof Servlet)  
  7.       ((Servlet) service).init(getServletConfig());  
  8. }  

根据当前服务类(impl)继承的接口(可以是标准的servlet接口也可以是Hessian中定义的Service接口)做相应的初始化(相当于spring的init标签属性); 

第二步.初始化远程服务类接口(基本上Hessian也是通过这接口知道自己对外暴露了哪些服务) 
Java代码  收藏代码
  1. if (_homeAPI != null) {  
  2.   }  
  3.   else if (getInitParameter("home-api") != null) {  
  4.     String className = getInitParameter("home-api");  
  5.   
  6.     _homeAPI = loadClass(className);  
  7.   }  
  8.   else if (getInitParameter("api-class") != null) {  
  9.     String className = getInitParameter("api-class");  
  10.   
  11.     _homeAPI = loadClass(className);  
  12.   }  
  13.   else if (_homeImpl != null) {  
  14.     _homeAPI = findRemoteAPI(_homeImpl.getClass());  
  15.   
  16.         if (_homeAPI == null)  
  17.          _homeAPI = _homeImpl.getClass();  
  18.   }  
  19. }  

其中home-api或是api-class定义了对外暴露的服务接口(服务的使用方需要依赖这个接口jar包); 

若在配置文件中未指定接口,首先尝试从当前服务类查找(findRemoteAPI); 

findRemoteAPI是一个递归方法:在类继承路径上递归查找只继承一个接口的类 
Java代码  收藏代码
  1. private Class findRemoteAPI(Class implClass)  
  2.   {  
  3.     if (implClass == null || implClass.equals(GenericService.class))  
  4.       return null;  
  5.       
  6.     Class []interfaces = implClass.getInterfaces();  
  7.   
  8.     if (interfaces.length == 1)  
  9.       return interfaces[0];  
  10.   
  11.     return findRemoteAPI(implClass.getSuperclass());  
  12.   }  

若查找失败,则使用当前服务类作为interface Class(这句话说得很奇怪,还是看代码比较明了) 

第三步.初始化 HessianSkeleton 类(相当于HessianExecutor) 
Java代码  收藏代码
  1. public HessianSkeleton(Object service, Class apiClass)  
  2. {  
  3.     super(apiClass);  
  4.   
  5.     _service = service;  
  6.   
  7.     if (! apiClass.isAssignableFrom(service.getClass()))  
  8.       throw new IllegalArgumentException("Service " + service + " must be an instance of " + apiClass.getName());  
  9. }  

重点在于super(apiClass):AbstractSkeleton的初始化 
Java代码  收藏代码
  1. protected AbstractSkeleton(Class apiClass)  
  2. {  
  3.     _apiClass = apiClass;  
  4.   
  5.     Method []methodList = apiClass.getMethods();  
  6.   
  7.     for (int i = 0; i < methodList.length; i++) {  
  8.       Method method = methodList[i];  
  9.   
  10.       if (_methodMap.get(method.getName()) == null)  
  11.         _methodMap.put(method.getName(), methodList[i]);  
  12.   
  13.       Class []param = method.getParameterTypes();  
  14.       String mangledName = method.getName() + "__" + param.length;  
  15.       _methodMap.put(mangledName, methodList[i]);  
  16.         
  17.       _methodMap.put(mangleName(method, false), methodList[i]);  
  18.     }  
  19. }  

Method []methodList = apiClass.getMethods(); 

获取服务接口中(_homeAPI)所有public方法,这也是为什么hessian说:每一个public方法都是一个远程服务。 

接着遍历所有方法,放入_methodMap中,这里有个关键点,如何支持重载? 

Hessian是这样处理的,比如对于一个方法: public void sayHello(String str),那么在_methodMap中会存放三个key(这三个key对应着同一个value,也就是sayHello方法) 
sayHello 
sayHello_1     (参数个数) 
sayHello_string    (参数类型)
 

所以只要client传递方法说明支持调用重载方法,那么他就会调用到正确的重载方法,不然结果是不确定的。 

service(ServletRequest request, ServletResponse response)
 

这个方法重写了父类(GenericServlet)的service方法 

第一步.检查请求方式是否是post 
Java代码  收藏代码
  1. if (! req.getMethod().equals("POST")) {  
  2.     res.setStatus(500"Hessian Requires POST");  
  3.     PrintWriter out = res.getWriter();  
  4.   
  5.     res.setContentType("text/html");  
  6.     out.println("<h1>Hessian Requires POST</h1>");  
  7.   
  8.     return;  
  9. }  

第二步.初始化Hessian输入流/输出流 
Java代码  收藏代码
  1. InputStream is = request.getInputStream();  
  2. OutputStream os = response.getOutputStream();  
  3.   
  4. HessianInput in = new HessianInput(is);  
  5. HessianOutput out = new HessianOutput();  
  6. out.setSerializerFactory(getSerializerFactory()); // 关于这里的序列化工厂,后面的文章会详述  
  7. out.init(os);  

第三步.调用服务(这就是RPC中的C哈 ^_^) 
Java代码  收藏代码
  1. _homeSkeleton.invoke(in, out);  

我们具体看下HessianSkeleton.invoke方法: 
Java代码  收藏代码
  1. //反序列化输入流获取调用的服务方法  
  2. String methodName = in.readMethod();  
  3. Method method = getMethod(methodName);  
  4.   
  5. //反序列输入流获取方法参数  
  6. Class []args = method.getParameterTypes();  
  7. Object []values = new Object[args.length];  
  8.   
  9. for (int i = 0; i < args.length; i++)  
  10.   values[i] = in.readObject(args[i]);  
  11.   
  12. // 真正的执行服务方法  
  13. result = method.invoke(_service, values);  
  14.   
  15.   
  16. //最后把执行写回输出流,输出起始标志:'r' 1 0  
  17. out.startReply();  
  18. // 序列化结果(远程调用结果)  
  19. out.writeObject(result);  
  20. // 输出结束标志'z'  
  21. out.completeReply();  

HessianServlet总结 
通过上面的源码分析,我们了解到: 

1.Hessian如何响应一个远程调用; 

2.在web.xml中的各项配置都是什么意思,以及Hessian相应的默认处理规则; 

3.Hessian如何支持方法重载; 
0 0
原创粉丝点击