服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)
我们到底能走多远系列(40)
扯淡:
判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了。
主题:
场景:项目A作为主项目,业务实现完整,项目B需要调用项目A中的部分服务,那么项目A就需要提供出服务出来。实现分布式调用的方法有很多,这里介绍一下利用Spring Http Invoker 来实现的服务提供和调用。
demo地址:摸我
如果你对快速用springmvc搭建web应用感兴趣:摸我
阅读Spring的源码基本的流程实现是这样的:

下面就详细分析一下这个流程的实现:
<bean id="remoteDemoService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl">
<value>http://localhost:7777/remote/demoService</value>
</property>
<property name="serviceInterface">
<value>com.witown.open.demo.remote.service.DemoService</value>
</property>
</bean>
首先看的是HttpInvokerProxyFactoryBean类,它继承FactoryBean,所以先来看一看这个FactoryBean。
public class HttpInvokerProxyFactoryBean extends HttpInvokerClientInterceptor
implements FactoryBean<Object> {
// 代理对象
private Object serviceProxy;
// 初始化后执行方法
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (getServiceInterface() == null) {
throw new IllegalArgumentException("Property 'serviceInterface' is required");
} // 使用AOP代理工厂创建代理对象,对这个代理对象的所有方法调用最后都会被拦截
// 调用接口的任何一个方法时都会被拦截去变成http请求
this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
}
// 获得bean方法
public Object getObject() {
return this.serviceProxy;
}
public Class<?> getObjectType() {
return getServiceInterface();
}
public boolean isSingleton() {
return true;
}
}
这样DemoService 的调用都会被HttpInvokerClientInterceptor拦截,拦截的方法调用会去执行HttpInvokerClientInterceptor中的invoke方法,这个方法明了的体现了客户端的执行流程的三个步骤:
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";
}
// 1,把方法执行信息封装成RemoteInvocation
RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
RemoteInvocationResult result = null;
try {
// 2,发起http请求,把方法执行信息传过去,正常的话,服务器会返回结果
result = executeRequest(invocation, methodInvocation);
}
catch (Throwable ex) {
throw convertHttpInvokerAccessException(ex);
}
try {
// 3,解析返回的结果,转化成java对象
return recreateRemoteInvocationResult(result);
}
catch (Throwable ex) {
if (result.hasInvocationTargetException()) {
throw ex;
}
else {
throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
"] failed in HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
}
}
}
public class RemoteInvocation implements Serializable {
//方法名
private String methodName;
// 类
private Class[] parameterTypes;
// 参数
private Object[] arguments;
// 步骤1中实际就是调用了这个构造函数而已
public RemoteInvocation(MethodInvocation methodInvocation) {
this.methodName = methodInvocation.getMethod().getName();
this.parameterTypes = methodInvocation.getMethod().getParameterTypes();
this.arguments = methodInvocation.getArguments();
}
// ...
}
public final RemoteInvocationResult executeRequest(HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception {
// RemoteInvocation(远程调用对象),转成可以传输的OutputStream,以便写入http请求中
ByteArrayOutputStream baos = getByteArrayOutputStream(invocation);
if (logger.isDebugEnabled()) {
logger.debug("Sending HTTP invoker request for service at [" + config.getServiceUrl() +
"], with size " + baos.size());
}
// 将流写入http请求,发送请求,并接收响应
return doExecuteRequest(config, baos);
}
继续追踪getByteArrayOutputStream方法,发现使用了ObjectOutputStream最终调用了writeObject方法来将对象转化成流,下面是一段ObjectOutputStream的描述:
protected RemoteInvocationResult doExecuteRequest(
HttpInvokerClientConfiguration config, ByteArrayOutputStream baos)
throws IOException, ClassNotFoundException {
// 打开远程的HTTP连接
HttpURLConnection con = openConnection(config); // 设置HTTP连接信息
prepareConnection(con, baos.size()); // 把准备好的序列化的远程方法调用对象的字节流写入到HTTP请求体中
writeRequestBody(config, con, baos); // 校验HTTP响应
validateResponse(config, con); // 获得HTTP相应体的流对象
InputStream responseBody = readResponseBody(config, con);
// 读取远程调用结果对象并返回
return readRemoteInvocationResult(responseBody, config.getCodebaseUrl());
}
最后的读取远程调用结果对象并返回:
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
// 获得Object
Object obj = ois.readObject();
if (!(obj instanceof RemoteInvocationResult)) {
throw new RemoteException("Deserialized object needs to be assignable to type [" +
RemoteInvocationResult.class.getName() + "]: " + obj);
}
// 服务端对返回的对象封装成RemoteInvocationResult!
return (RemoteInvocationResult) obj;
}
到这里基本完成了,客户端请求远程对象方法的流程。
<servlet>
<servlet-name>remote</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/config/remote-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>remote</servlet-name>
<url-pattern>/remote/*</url-pattern>
</servlet-mapping>
remote-servlet.xml文件中配置了处理请求的service:
< bean name= "/demoService" class ="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" >
< property name= "service" ref = "demoServiceImpl"/>
< property name= "serviceInterface" value ="com.witown.open.demo.service.DemoService" />
</bean >
HttpInvokerServiceExporter继承了HttpRequestHandler,所以请求从handleRequest方法开始。
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { try {
// 解析请求,获得RemoteInvocation
RemoteInvocation invocation = readRemoteInvocation(request);
// 根据RemoteInvocation里的信息:方法,参数,执行,把返回的结果封装成RemoteInvocationResult
RemoteInvocationResult result = invokeAndCreateResult(invocation, getProxy());
// 把结果写入响应
writeRemoteInvocationResult(request, response, result);
}
catch (ClassNotFoundException ex) {
throw new NestedServletException("Class not found during deserialization", ex);
}
}
内部调用的代码经过前面请求调用发起的流程学习,就会很好理解了。
至此,整个流程就完整了。
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)的更多相关文章
- 定时任务管理中心(dubbo+spring)-我们到底能走多远系列47
我们到底能走多远系列47 扯淡: 又是一年新年时,不知道上一年你付出了多少,收获了多少呢?也许你正想着老板会发多少奖金,也许你正想着明年去哪家公司投靠. 这个时间点好好整理一下,思考总结一下,的确是个 ...
- Spring mvc源码url路由-我们到底能走多远系列(38)
我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ...
- Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33) 扯淡: 各位: 命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪 心酸 更不应舍弃 ... 主题: Spring源码阅读还在继 ...
- 初始化IoC容器(Spring源码阅读)-我们到底能走多远系列(31)
我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? 毕竟,对自己的收入的分配差不多体现了自己的 ...
- JMS生产者+单线程发送-我们到底能走多远系列(29)
我们到底能走多远系列(29) 扯淡: “然后我俩各自一端/望着大河弯弯/终于敢放胆/嘻皮笑脸/面对/人生的难” --- <山丘> “迎着风/迎向远方的天空/路上也有艰难/也有那解 ...
- node实现http上传文件进度条 -我们到底能走多远系列(37)
我们到底能走多远系列(37) 扯淡: 又到了一年一度的跳槽季,相信你一定准备好了,每每跳槽,总有好多的路让你选,我们的未来也正是这一个个选择机会组合起来的结果,所以尽可能的找出自己想要的是什么再做决定 ...
- node模拟http服务器session机制-我们到底能走多远系列(36)
我们到底能走多远系列(36) 扯淡: 年关将至,总是会在一些时间节点上才感觉时光飞逝,在平时浑浑噩噩的岁月里都浪费掉了太多的宝贵.请珍惜! 主题: 我们在编写http请求处理和响应的代码的时 ...
- Sharded实现学习-我们到底能走多远系列(32)
我们到底能走多远系列(32) 扯淡: 工作是容易的赚钱是困难的 恋爱是容易的成家是困难的 相爱是容易的相处是困难的 决定是容易的可是等待是困难的 主题: 1,Sharded的实现 Sharded ...
- Spring3整合Hibernate4-我们到底能走多远系列(30)
我们到底能走多远系列(30) 扯淡: 30篇啦!从2012-08-15开始的系列,东平西凑将近一年的时间也就这么几篇.目标的100篇,按这个速度也要再搞两年呢. 发博客果然不是件容易的事,怪不得更多的 ...
随机推荐
- 检索 COM 类工厂中 CLSID 为 {10020200-E260-11CF-AE68-00AA004A34D5} 的组件时失败,解决方法如下:
检索 COM 类工厂中 CLSID 为 {10020200-E260-11CF-AE68-00AA004A34D5} 的组件时失败,解决方法如下: 第 一步:首先将msvcr71.dll, SQLD ...
- DP 剪枝
DP其实也是和搜索一样可以有剪枝的,昨晚看到一个超级好的DP剪枝题:(HDU - 5009) N段东东,要染色,每次给一个区间染色需要的花费为 该区间颜色总数的平方. 每一段只能被染一次色.求 最 ...
- php unicode
在很多场合能看到unicode编码过的文字,如“\u6d3b\u52a8\u63a5\u53e3”,虽然程序会认识,但人眼无法阅读,很不方便,网络上很多人写了很多的转换函数,但是一个比一个臃肿,终于发 ...
- Linux - gcc和g++的区别
一般linux系统都自带了gcc编译器的,你可以用你的安装光盘去安装,如果你是觉得自带的gcc版本太低了,可以去gcc的官方网站可以下载到,编译需要很长的时间,如果你只编译C或者C++可以只下载gcc ...
- java枚举类
enum关键字用于定义枚举类,若枚举只有一个成员, 则可以作为一种单例模式的实现方式. 枚举类对象的属性不应允许被改动, 所以应该使用 private final 修饰. 枚举类的使用 priva ...
- JavaScript 数组方法和属性
一. 数组对象的操作方法 1. 数组的创建 2.prototype属性 返回对象原型的引用,prototype属性时object共有的. objectName.prototype,其中objectNa ...
- const的全面理解
const关键字用来作甚?const是一个类型修饰符.常见的类型修饰符有哪些? short long unsigned signed static autoextern register 定义一个变量 ...
- ProcessOn:功能强大的在线作图工具(HTML5)
ProcessOn是一款专业作图人员的社交网络,这里汇聚很多业界专家.学者,同时他们分享的作品又形成一个庞大的知识图库,你在学习专业知识的同时还可以结交一些志同道合的新朋友. ProcessOn核心设 ...
- scst使用的一些问题
1,编译问题 问题描述: [root@localhost scstadmin]# make cd scstadmin && make all ]: Entering directory ...
- (五)CoreData 使用 (转)
第一次真正的使用CoreData,因此也会写下体会和心得...等有时间 Core Data数据持久化是对SQLite的一个升级,它是ios集成的,在说Core Data之前,我们先说说在CoreDat ...