远程服务HttpInvoker源码解析(四)服务端实现

来源:互联网 发布:一级域名怎么解析二级 编辑:程序博客网 时间:2024/06/03 09:06

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

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

我们首先看看HttpInvoker的使用示例。HttpInvoker是基于HTTP的远程调用,同时也是使用Spirng中提供的web服务作为基础,所以我们的测试需要首先搭建web工程。

关于HttpInvoker的使用示例有兴趣的朋友可以去网上搜索,很简单,下面我们从源码解析开始分析


服务端实现


对于Spring中HttpInvoker服务的实现,我们还是首先从服务端进行分析。

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"><bean name="httpinvokertest" class="org.tarena.note.service.impl.HttpInvokertestImpl"/><bean name="/hit" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"><property name="service" ref="httpinvokertest"/><property name="serviceInterface" value="org.tarena.note.service.HttpInvokerTest1"/></bean></beans>


根据xml中的配置,我们分析入口类应该为

org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
,那么同样,根据这个函数进行分析:

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

1.创建代理

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

通过将上面3个方法串联,可以看到,初始化过程中实现的逻辑主要是创建了一个代理,代理中封装了对于特定请求的处理方法以及接口等信息,而这个代理的最关键目的是加入了RemoteInvocationTraceInterceptor增强器,当然创建代理还有些其他好处,比如代码优雅,方便扩展等。RemoteInvocationTraceInterceptor中的增强主要是对增强的目标方法进行一些相关信息的日志打印,并没有在此基础上进行任何功能性的增强。那么这个代理究竟是在什么时候使用的呢?暂时留下悬念,我们接下来分析当有Web请求时HttpRequestHandler的handleRequest方法的处理

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

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

(1)从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    {        try{       ObjectInputStream ois = createObjectInputStream(decorateInputStream(request, is));        RemoteInvocation remoteinvocation = doReadRemoteInvocation(ois);        }finally{        <pre name="code" class="java">           ois.close();

} }

    protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois)        throws IOException, ClassNotFoundException    {        Object obj = ois.readObject();        if(!(obj instanceof RemoteInvocation))            throw new RemoteException((new StringBuilder()).append("Deserialized object needs to be assignable to type [").append(org/springframework/remoting/support/RemoteInvocation.getName()).append("]: ").append(obj).toString());        else            return (RemoteInvocation)obj;    }
对于序列化提取与转换过程其实并没有太多需要解释的东西,这里完全是按照标准的方式进行操作,包括创建ObjectInputStream以及从ObjectInputStream中提取对象实例。

(2)执行调用。

根据反序列化方式得到的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类进行统一封装。

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

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

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




0 0
原创粉丝点击