• SpringHttpInvoker解析2-服务端实现


    主要的配置文件

    <!-- 在Spring的httpInvoker服务 -->  
    <bean id="httpInvokerUserService"  
        class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">  
        <!--需要发布的实现类 -->  
        <property name="service" ref="userServiceHttpInvoker" />  
        <property name="serviceInterface" value="com.gosun.jws.httpinvoker.UserService" />  
    </bean>  
    <bean id="userServiceHttpInvoker" class="com.gosun.jws.httpinvoker.UserServiceImpl" /> 

    我们分析入口类应该为HttpInvokerServiceExporter,RemoteInvocationSerializingExporter类实现了接口InitializingBean接口,本类实现了HttpRequestHandler接口。当某个bean继承自InitializingBean接口的时候,Spring会确保这个bean在初始化时调用其afterPropertiesSet方法,而对于HttpRequestHandler接口,因为我们在配置中已经将此接口配置成Web服务,那么当有相应请求的时候,Spring的Web服务就会将程序引导至HttpRequestHandler的handlerRequest方法中。首先我们从afterPropertiesSet方法开始分析,看看在bean的初始化过程中做了那些逻辑。

    public class HttpInvokerServiceExporter extends RemoteInvocationSerializingExporter  
        implements HttpRequestHandler 

    创建代理

    public void afterPropertiesSet()  
    {  
        prepare();  
    }  
    public void prepare()  
    {  
        proxy = getProxyForService();  
    }  
    protected Object getProxyForService()  
    {  
        //验证service  
       checkService();  
        //验证serviceInterface  
       checkServiceInterface();  
        //使用JDK的方式创建代理  
       ProxyFactory proxyFactory = new ProxyFactory();  
        //添加代理接口  
       proxyFactory.addInterface(getServiceInterface());  
        if(registerTraceInterceptor == null ? interceptors == null : registerTraceInterceptor.booleanValue())  
            proxyFactory.addAdvice(new RemoteInvocationTraceInterceptor(getExporterName()));  
        if(interceptors != null)  
        {  
            AdvisorAdapterRegistry adapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();  
            for(int i = 0; i < interceptors.length; i++)  
                 //加入代理的横切面RemoteInvocationTraceInterceptor并记录Exporter名称  
                proxyFactory.addAdvisor(adapterRegistry.wrap(interceptors[i]));  
      
        }  
        //设置要代理的目标类  
       proxyFactory.setTarget(getService());  
       proxyFactory.setOpaque(true);  
        //创建代理  
       return proxyFactory.getProxy(getBeanClassLoader());  
    }  

    可以看到,初始化过程中实现的逻辑主要是创建了一个代理,代理中封装了对于特定请求的处理方法以及接口等信息,而这个代理的最关键目的是加入了RemoteInvocationTraceInterceptor增强器,当然创建代理还有些其他好处,比如代码优雅,方便扩展等。RemoteInvocationTraceInterceptor中的增强主要是对增强的目标方法进行一些相关信息的日志打印,并没有在此基础上进行任何功能性的增强

    处理来自客户端的request

    当有web请求时,根据配置中的规则会把路径匹配的访问直接引入对应的HttpRequestHandler中。

    public void handleRequest(HttpServletRequest request, HttpServletResponse response)  
        throws ServletException, IOException  
    {  
        try  
        {  
            //从request中读取序列化对象  
           RemoteInvocation invocation = readRemoteInvocation(request);  
            //执行调用  
           RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());  
            //将结果的序列化对象写入输出流  
           writeRemoteInvocationResult(request, response, result);  
        }  
        catch(ClassNotFoundException ex)  
        {  
            throw new NestedServletException("Class not found during deserialization", ex);  
        }  
    }  

    在handlerRequest函数中,很清楚地看到了HttpInvoker处理的大致框架,HttpInvoker服务简单点说就是将请求的方法,也就是RemoteInvocation对象,从客户端序列化并通过Web请求出入服务端,服务端在对传过来的序列化对象进行反序列化还原RemoteInvocation实例,然后通过实例中的相关信息进行相关方法的调用,并将执行结果再次的返回给客户端。从handlerRequest函数中我们也可以清晰地看到程序执行的框架结构。

    从request中读取序列化对象

    主要是从HttpServletRequest提取相关的信息,也就是提取HttpServletRequest中的RemoteInvocation对象的序列化信息以及反序列化的过程。

      protected RemoteInvocation readRemoteInvocation(HttpServletRequest request)  
          throws IOException, ClassNotFoundException  
      {  
          return readRemoteInvocation(request, ((InputStream) (request.getInputStream())));  
      }  
        protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is)
                throws IOException, ClassNotFoundException {
    
            ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is));
            try {
                return doReadRemoteInvocation(ois);
            }
            finally {
                ois.close();
            }
        }
        protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)
                throws IOException, ClassNotFoundException {
    
            Object obj = ois.readObject();
            if (!(obj instanceof RemoteInvocation)) {
                throw new RemoteException("Deserialized object needs to be assignable to type [" +
                        RemoteInvocation.class.getName() + "]: " + obj);
            }
            return (RemoteInvocation) obj;
        }

    这里完全是按照标准的方式进行操作,包括创建ObjectInputStream以及从ObjectInputStream中提取对象实例。

    执行调用

    根据反序列化方式得到的RemoteInvocation对象中的信息进行方法调用。注意,此时调用的实体并不是服务接口或者服务类,而是之前在初始化的时候构造的封装了服务接口以及服务类的代理。完成了RemoteInvocation实例的提取,也就意味着可以通过RemoteInvocation实例中提供的信息进行方法调用了。

    protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation, Object targetObject)  
    {  
        try  
        {  
            //激活代理类中对应invacation中的方法  
           Object value = invoke(invocation, targetObject);  
            //封装结果以便于序列化  
           return new RemoteInvocationResult(value);  
        }  
        catch(Throwable ex)  
        {  
            return new RemoteInvocationResult(ex);  
        }  
    }  

    对应方法的激活也就是invoke方法的调用,虽然经过层层环绕,但是最终还是实现了一个我们熟知的调用invocation.invoke(targetObject),也就是执行RemoteInvocation类中的invoke方法,大致的逻辑还是通过RemoteInvocation中对应的方法信息在targetObject上去执行,此方法在分析RMI功能的时候已经分析过。但是在对于当前方法的targetObject参数,此targetObject是代理类,调用代理类的时候需要考虑增强方法的调用

    对于返回结果需要使用RemoteInvocationResult进行封装,之所以需要通过使用RemoteInvocationResult类进行封装,是因为无法保证对于所有操作的返回结果都继承Serializable接口,也就是说无法保证所有返回结果都可以直接进行序列化。那么,就必须使用RemoteInvocationResult类进行统一封装。

    将结果的序列化对象写入输出流

    protected void writeRemoteInvocationResult(HttpServletRequest request, HttpServletResponse response,
             RemoteInvocationResult result)
    throws IOException { response.setContentType(getContentType()); writeRemoteInvocationResult(request, response, result, ((OutputStream) (response.getOutputStream()))); } protected void writeRemoteInvocationResult(HttpServletRequest request, HttpServletResponse response,
             RemoteInvocationResult result, OutputStream os)
    throws IOException { //获取输出流 ObjectOutputStream oos = createObjectOutputStream(decorateOutputStream(request, response, os)); try{ //将序列化对象写入输出流 doWriteRemoteInvocationResult(result, oos); }finally{ oos.close(); } } protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos) throws IOException { oos.writeObject(result); }
  • 相关阅读:
    HDU 4315 Climbing the Hill [阶梯Nim]
    POJ 1704 Georgia and Bob [阶梯Nim]
    BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏 [Nim游戏 SG函数]
    BZOJ 1299: [LLH邀请赛]巧克力棒 [组合游戏]
    浏览器缓存知识点总结
    软件测试自动化的最新趋势
    性能测试面试题(附答案
    最流行的自动化测试工具,总有一款适合你
    49种软件测试方法
    linux执行jmeter脚本解决响应数据为空
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/6088750.html
Copyright © 2020-2023  润新知