经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
Spring HttpInvoker 从实战到源码追溯
来源:cnblogs  作者:Orson  时间:2018/10/31 9:09:18  对本文有异议

   Spring HttpInvoker 作为 Spring 家族的一员,作为新的远程调用模型。

   主要目的是来执行基于 HTTP 的远程调用(轻松穿越防火墙),并使用标准的 JDK 序列化机制。

   Http 远程调用框架不是有成熟的 Hessian、Burlap嘛,Spring 团队为什么还要重复造轮子呢? 

   是因为他们有各自的序列化方式,当数据类型比较复杂时私有的序列化方法可能会无法胜任。

1.项目实战

   

   分为 server 服务和 api 接口模块,api 模块打包方式为 jar,其中定义对应传递 BO 和接口。

   server 模块打包方式为 war,为业务核心服务,进行业务逻辑。

   接口定义如下:

  1. public interface UserService {
  2. /**
  3. * 通过ID获取用户
  4. *
  5. * @param uuid 用户ID
  6. * @return 用户实体
  7. */
  8. User getUserById(String uuid);
  9. }

   接口返回的业务实体属性,还需你根据具体业务拿捏,实现类:

  1. public class UserServiceImpl implements UserService {
  2. @Override
  3. public User getUserById(String uuid) {
  4. User user = new User();
  5. user.setUuid(uuid);
  6. user.setName("Orson");
  7. user.setPasswd("xyxy");
  8. user.setSex("F");
  9. user.setPhone("13974856211");
  10. user.setPhoto("/photo/user/xyxy.gif");
  11. user.setEmail("954875698@qq.com");
  12. user.setCreateBy("orson");
  13. return user;
  14. }
  15. }

   Spring 配置服务如下:

  1. <bean id="userServiceImpl" class="com.rambo.httpinvoker.server.impl.UserServiceImpl" />
  2.  
  3. <bean id="userServiceInvoker" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
  4. <property name="service" ref="userServiceImpl" />
  5. <property name="serviceInterface" value="com.rambo.httpinvoker.api.UserService" />
  6. </bean>
  7.  
  8. <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  9. <property name="mappings">
  10. <props>
  11. <prop key="/userService">userServiceInvoker</prop>
  12. </props>
  13. </property>
  14. </bean>

   web.xml 配置:

  1. <servlet>
  2. <servlet-name>service</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>classpath:spring-httpinvoke-server.xml</param-value>
  7. </init-param>
  8. <load-on-startup>1</load-on-startup>
  9. </servlet>
  10.  
  11. <servlet-mapping>
  12. <servlet-name>service</servlet-name>
  13. <url-pattern>/service/*</url-pattern>
  14. </servlet-mapping>

   启动服务模块,这时服务就已经发布成功了,是不是很简单?

   客户端将 api 依赖进去,spring 稍做下配置即可以在客户端中使用对应的服务。

  1. <!-- 客户端使用 HttpInvokerProxyFactoryBean 代理客户端向服务器端发送请求,请求接口为 UserService 的服务 -->
  2. <bean id="userService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
  3. <property name="serviceUrl" value="http://${server.url}/service/userService"/>
  4. <property name="serviceInterface" value="com.rambo.httpinvoker.api.UserService"/>
  5. </bean>

   demo 项目地址:https://gitee.com/LanboEx/rmi-demo.git

2.源码分析

   源码分析时从客户端和服务端配置两个对象 HttpInvokerServiceExporter、HttpInvokerProxyFactoryBean下手。

   HttpInvokerServiceExporter 继承 HttpRequestHandler 并实现 handleRequest 方法。

  1. public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. try {
  3. RemoteInvocation invocation = this.readRemoteInvocation(request);
  4. RemoteInvocationResult result = this.invokeAndCreateResult(invocation, this.getProxy());
  5. this.writeRemoteInvocationResult(request, response, result);
  6. } catch (ClassNotFoundException var5) {
  7. throw new NestedServletException("Class not found during deserialization", var5);
  8. }
  9. }

   首先从 http 请求中读取远程调用对象,然后调用服务对应方法并组织执行结果,最后将执行结果写入到 http 返回。   

   这几个过程你追溯到底层代码,你会发现 Java ObjectInputStream 反序列化对象、Java Method 反射对象。

    HttpInvokerProxyFactoryBean 实现 FactoryBean 接口并继承 HttpInvokerClientInterceptor,spring ioc 托管该类并初始化对应属性后返回该类代理。

  1. public void afterPropertiesSet() {
  2. super.afterPropertiesSet();
  3. if (this.getServiceInterface() == null) {
  4. throw new IllegalArgumentException("Property 'serviceInterface' is required");
  5. } else {
  6. this.serviceProxy = (new ProxyFactory(this.getServiceInterface(), this)).getProxy(this.getBeanClassLoader());
  7. }
  8. }

   注意获取代理类时传入的拦截器参数为 this 即为父类 HttpInvokerClientInterceptor。

   该拦截器 invoke 方法首先进行远程调用对象的封装,其次发起远程服务请求,最后解析返回结果并封装返回。

   追溯这几个过程的时候你会看到,Sprng 代理对象 ProxyFactory、Java 序列对象 ObjectOutputStream、Java Http 连接对象 HttpURLConnection。

   HttpInvoker 调优时也记得去关注上述几个对象:https://blog.csdn.net/qian_348840260/article/details/51555864

   从服务暴露到服务调用,debug 源码过来底层总是那些熟悉的面孔,只不过 Spring 团队做了出色的封装和合理的抽象。

  1. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  2. if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
  3. return "HTTP invoker proxy for service URL [" + this.getServiceUrl() + "]";
  4. } else {
  5. RemoteInvocation invocation = this.createRemoteInvocation(methodInvocation);
  6. RemoteInvocationResult result;
  7. try {
  8. result = this.executeRequest(invocation, methodInvocation);
  9. } catch (Throwable var7) {
  10. RemoteAccessException rae = this.convertHttpInvokerAccessException(var7);
  11. throw (Throwable)(rae != null ? rae : var7);
  12. }
  13. return this.recreateRemoteInvocationResult(result);
  14. }
  15. }

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号