主要的配置文件

<!-- 在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. IOS - UITableViewCell的选中时的颜色及tableViewCell的selecte与deselecte

    1.系统默认的颜色设置 [cpp] view plaincopy //无色 cell.selectionStyle = UITableViewCellSelectionStyleNone; //蓝色 ...

  2. ios cell展示可滑动的图片

    需求: 点击cell上的图片.图片以原图显示出来,可以放大或缩小.再次点击图片移除图片显示原来界面.(和QQ空间看图片类似) 点击图片实现效果: 1. 自定义一个 UITableView (KDIma ...

  3. python基础——获取对象信息

    python基础——获取对象信息 当我们拿到一个对象的引用时,如何知道这个对象是什么类型.有哪些方法呢? 使用type() 首先,我们来判断对象类型,使用type()函数: 基本类型都可以用type( ...

  4. iOS 8 AutoLayout与Size Class

    转自:http://www.cocoachina.com/ios/20141217/10669.html 前言 iOS8 和iPhone6发布已经过去蛮久了,广大的果粉终于迎来了大屏iPhone,再也 ...

  5. postgresql导入及导出

    导入命令 psql -d GAME -U postgres -f /root/plubic.sql 出现如下错误: psql: FATAL:  Peer authentication failed f ...

  6. 四、优化及调试--网站优化--Yahoo军规中

    8.避免使用CSS表达式(避免在CSS中使用Expressions) 什么是CSS表达式:是用来把CSS属性和JavaScript关联起来.

  7. 随机数组&大数相加

    随机生成10个数,填充一个数组,然后用消息框显示数组内容,接着计算数组元素的和,将结果也显示在消息框中 一,      设计思路: 先生成随机数数组,再将数组保存在一个字符串中,然后将数组各数字加和, ...

  8. 对Java内存模型即JMM的理解

    类似物理上的计算机系统,Java虚拟机规范中也定义了一种Java内存模型,即Java Memory Model(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能 ...

  9. Avalon学习

    1.认识AvalonAvalon是一个简单易用的迷你的MVVM框架,作者是博客园的司徒正美,去哪儿.搜狐等等都用这个框架.没有任何依赖,兼容性非常好,支持IE6,不到5000行,压缩后不到50KB.官 ...

  10. ExcelReport第三篇:扩展元素格式化器

    导航 目   录:基于NPOI的报表引擎——ExcelReport 上一篇:ExcelReport源码解析 概述 上篇中已介绍了ExcelRepor的架构,本篇将通过例子讲述如何扩展元素格式化器以满足 ...