博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HttpInvoker-----服务端实现
阅读量:5093 次
发布时间:2019-06-13

本文共 6430 字,大约阅读时间需要 21 分钟。

前言

Spring开发小组意识到在RMI服务和基于HTTP的服务(如Hessian和Burlap)之间的空白。一方面,RMI使用Java标准的对象序列化,但很难穿越防火墙;另一方面。Hessian/Burlap能很好的穿过防火墙工作,但是使用自己一套的对象序列化机制。

就这样,Spring的HttpInvoker应运而生。HttpInvoker是一个新的远程调用模型,作为Spring框架的一部分,来执行基于HTTP的远程调用(让防火墙高兴的事),并使用Java的序列化机制(这是Java程序员高兴的事)。

HttpInvoker是基于HTTP的远程调用,同时也是以Spring提供的web服务作为基础。

服务端实现

服务端的入口为org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter,那么同样的,先来看一下这个类的类图:

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

1.创建代理

 跟踪代码,创建代理的核心代码在getProxyForService方法中。

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

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

2.处理来自客户端的request

 当有web请求时,根据配置中的规则会把路径匹配的访问直接引入对应的HttpRequestHandler中。本例中的请求与普通的web请求时有些区别的,因为此处的请求包含着HttpInvoker的处理过程。

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);        }    }

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

(1)从request中读取序列化对象

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

protected RemoteInvocation readRemoteInvocation(HttpServletRequest request)            throws IOException, ClassNotFoundException {        return readRemoteInvocation(request, 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() + "]: " + ClassUtils.getDescriptiveType(obj));        }        return (RemoteInvocation) obj;    }

 对于序列化的提取与转换过程其实没有太多需要解释的东西,这里完全是按照标准的方式进行操作,包括创建ObjectInputStream以及从ObjectInputStream中提取对象实例。

(2)执行调度

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

完成了RemoteInvocation实例的提取,也就意味着可以通过RemoteInvocation实例中提供的信息进行方法调用了。

protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation, Object targetObject) {        try {            //激活代理类中对应Invocation中的方法            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类进行统一封装。

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

同样这里也包括结果的序列化过程。

protected void writeRemoteInvocationResult(            HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result)            throws IOException {        response.setContentType(getContentType());        writeRemoteInvocationResult(request, response, result, response.getOutputStream());    }
protected void writeRemoteInvocationResult(            HttpServletRequest request, HttpServletResponse response, RemoteInvocationResult result, OutputStream os)            throws IOException {        //获取输入流        ObjectOutputStream oos =                createObjectOutputStream(new FlushGuardedOutputStream(decorateOutputStream(request, response, os)));        try {            //将序列化对象写入输入流            doWriteRemoteInvocationResult(result, oos);        }        finally {            oos.close();        }    }
protected void doWriteRemoteInvocationResult(RemoteInvocationResult result, ObjectOutputStream oos)            throws IOException {        oos.writeObject(result);    }

至此,HttpInvoker的服务器端解析已经结束。

参考:《Spring源码深度解析》 郝佳 编著:

转载于:https://www.cnblogs.com/Joe-Go/p/10310241.html

你可能感兴趣的文章
阿里云服务器CentOS6.9安装Mysql
查看>>
剑指offer系列6:数值的整数次方
查看>>
js 过滤敏感词
查看>>
poj2752 Seek the Name, Seek the Fame
查看>>
软件开发和软件测试,我该如何选择?(蜗牛学院)
查看>>
基本封装方法
查看>>
bcb ole拖拽功能的实现
查看>>
生活大爆炸之何为光速
查看>>
bzoj 2456: mode【瞎搞】
查看>>
[Typescript] Specify Exact Values with TypeScript’s Literal Types
查看>>
[GraphQL] Reuse Query Fields with GraphQL Fragments
查看>>
Illustrated C#学习笔记(一)
查看>>
理解oracle中连接和会话
查看>>
两种最常用的Sticky footer布局方式
查看>>
Scrapy实战篇(三)之爬取豆瓣电影短评
查看>>
HDU 5510 Bazinga KMP
查看>>
[13年迁移]Firefox下margin-top问题
查看>>
Zookeeper常用命令 (转)
查看>>
Enterprise Library - Data Access Application Block 6.0.1304
查看>>
重构代码 —— 函数即变量(Replace temp with Query)
查看>>