主要的配置文件

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

SpringHttpInvoker解析2-服务端实现的更多相关文章

  1. Netty 4源码解析:服务端启动

    Netty 4源码解析:服务端启动 1.基础知识 1.1 Netty 4示例 因为Netty 5还处于测试版,所以选择了目前比较稳定的Netty 4作为学习对象.而且5.0的变化也不像4.0这么大,好 ...

  2. SpringHttpInvoker解析3-客户端实现

    主要的配置文件 <bean id="httpInvokerUserService" class="org.springframework.remoting.http ...

  3. Spring Cloud系列(三):Eureka源码解析之服务端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-starter-netflix-eureka-ser ...

  4. Netty源码解析 -- 服务端启动过程

    本文通过阅读Netty源码,解析Netty服务端启动过程. 源码分析基于Netty 4.1 Netty是一个高性能的网络通信框架,支持NIO,OIO等多种IO模式.通常,我们都是使用NIO模式,该系列 ...

  5. 如何通过JavaScript构建Asp.net服务端控件

    摘要 虽然ASP.NET的服务器控件一直被大家所诟病,但是用户控件(ACSX)在某些场景下还是非常有用的. 在一些极特珠的情况下,我们会使用JavaScript动态的构建页面中的控件,但假设遇到了我要 ...

  6. Watcher详解 工作机制, Watcher客户端注册、Watcher 服务端注册

    Watcher详解.接口 在 ZooKeeper 中, 接口类 Watcher 用于表示一个标注你的事件处理器,其定义了事件通知相关的逻辑,包含 KeeperState 和 EventType 两个枚 ...

  7. IoTClient开发4 - ModBusTcp协议服务端模拟

    前言 上篇我们实现了ModBusTcp协议的客户端读写,可是在很多时候编写业务代码之前是没有现场环境的.总不能在客户现场去写代码,或是蒙着眼睛写然后求神拜佛不出错,又或是在办公室部署一套硬件环境.怎么 ...

  8. ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析

    ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...

  9. 服务端提供的JSON数据接口与用户端接收解析JSON数据

    JSON格式的服务接口:http://www.cnblogs.com/visec479/articles/4118338.html 首先来了解下JSON格式解析 json结构的格式就是若干个 键/值( ...

随机推荐

  1. Effective C++ -----条款34:区分接口继承和实现继承

    接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. pure virtual函数只具体指定接口继承. 简朴的(非纯)impure vir ...

  2. 【leetcode】 Search a 2D Matrix (easy)

    Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...

  3. 【gsl】生成随机数

    来自:http://hsxqwanting.blog.163.com/blog/static/16945437201301042830815/ 使用GSL生成随机数时的三个步骤:    (1)gsl_ ...

  4. September 2nd 2016 Week 36th Friday

    How does the world look through your eyes? 你眼里的世界是什么样子的? How does the world look through your eyes? ...

  5. Mysql之日志恢复

    对于Mysql,每一步操作都会有相应记录,如insert,update,delete ,drop ,alter等相关DDL或DML操作.有时难免会出错,但在出错时如何恢复以复原数据. 例如,现在有这些 ...

  6. 从xml中构建sqlSessionFactory

    String resource = "org/mybatis/example/Configuration.xml"; Reader reader = Resources.getRe ...

  7. LeetCode : 223. Rectangle Area

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABRQAAAQ0CAYAAAAPPZBqAAAMFGlDQ1BJQ0MgUHJvZmlsZQAASImVlw

  8. vim 打开高亮和关闭高亮

    :set hls 找开高亮 :set nohls 关闭高亮

  9. execl一个工作薄中有几个个工作表,将这几个个工作表分别保存到不同execl文件中

    用宏运行: Sub QEJebel()    Dim sh As Worksheet    Dim Pa As String    Pa = ThisWorkbook.Path    For Each ...

  10. 20.策略者模式(Stragety Pattern)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...