Spring源码分析总结——HttpInvoker

该文章基于《Spring源码深度解析》撰写,感谢郝佳老师的奉献

HttpInvoker

因为RMI使用标准的Java标准的对象私有化,很难穿越防火墙,但是Hessian/Burlap因为是基于HTTP的服务却能很好的穿越防火墙。
在这种情况下,Spring的HttpInvoker应运而生,基于HTTP的远程调用并且使用Java序列化机制,现在给出小Demo:
首先其代码结构如下
代码架构
下面给出源代码

<!--web.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/remoting-servlet.xml</param-value>
    </context-param>


    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Test httpInvoker configuration -->
    <servlet>
        <servlet-name>remoting</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>remoting</servlet-name>
        <url-pattern>/remoting/*</url-pattern>
    </servlet-mapping>
</web-app>
<!--remoting-servlet.xml-->
<?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.0.xsd">
    <bean name="/hit" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
        <property name="service" ref="httpinvokertest"/>
        <property name="serviceInterface" value="HttpInvoker.HttpInvokerTestI"/>
    </bean>
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hit">/hit</prop>

            </props>
        </property>
    </bean>
    <bean name="httpinvokertest" class="HttpInvoker.Impl.HttpInvokertestImpl"/>
</beans>
<!--client.xml-->
<?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.0.xsd">
    <bean id="remoteService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
        <property name="serviceUrl" value="http://localhost:8080/remoting/hit"/>
        <property name="serviceInterface" value="HttpInvoker.HttpInvokerTestI"/>
    </bean>
</beans>
/*HttpInvokerTestI.java*/
package HttpInvoker;

public interface HttpInvokerTestI {
    public String getTestPo(String desp);
}
/*HttpInvokertestImpl.java*/
package HttpInvoker.Impl;

import HttpInvoker.HttpInvokerTestI;

public class HttpInvokertestImpl implements HttpInvokerTestI {
    @Override
    public String getTestPo(String desp) {
        return "getTestPo"+desp;
    }
}
/*Test.java*/
import HttpInvoker.HttpInvokerTestI;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("client.xml");
        HttpInvokerTestI httpInvokerTestI = (HttpInvokerTestI)context.getBean("remoteService");
        System.out.println(httpInvokerTestI.getTestPo("work"));
    }
}

首先启动服务器,然后运行Test文件,可以看见如下结果:
这里写图片描述

服务器端实现

根据remote-servlet.xml中我们可以看见HttpInvokerServiceExporter应该就是服务器端实现的重要类,其类层次结构如下:
HttpInvokerServiceExporter类层次
通过观察其类层次结构可以看到几个我们关注的接口:InitializingBean(用于初始化Bean时调用afterpropertiesSet方法)HttpRequestHandler(当有请求时,通过调用handleRequest方法进行处理)

首先从InitializingBean的afterpropertiesSet开始进行分析:在初始化时,通过JDK的方式创建了代理,创建代理的主要目的时加入RemoteInvocationTraceInterceptor增强器,主要用于对增强对象的目标方法进行日志的打印

然后分析HttpRequestHandler的handleRequest,该过程的调用比较复杂:
(1)从request中读取序列化信息,主要是从HttpServletRequest中提取RemoteInvocation对象的序列化以及反序列化的过程
(2)执行调用该方法比较重要直接给出源代码:

protected RemoteInvocationResult invokeAndCreateResult(RemoteInvocation invocation, Object targetObject) {
    try {
        Object value = this.invoke(invocation, targetObject);
        return new RemoteInvocationResult(value);
    } catch (Throwable var4) {
        return new RemoteInvocationResult(var4);
    }
}

该方法有两个需要说明的地方:1.this.invoke(invocation, targetObject);在调用时话考虑代理类targetObject的增强方法;2.返回的结果统一使用RemoteInvocationResult进行封装,原因是因为无法保证所有结果都能够直接进行序列化
(3)将结果的序列化对象写入输出流

客户端实现

服务器端一直提到的RemoteInvocation,是从HttpServletRequest中提取出来的,所以我们对于客户端的分析,如何创建RemoteInvocation将成为一个非常重要的问题,但是我们还是先锁定HttpInvokerProxyFactoryBean,查看其类层次结构:
HttpInvokerProxyFactoryBean类层次结构
几个重要的常见的接口就不再进行介绍了,下面仍然针对InitalizingBean接口分析初始化过程中的逻辑,HttpInvokerProxyFactoryBean在初始化的过程中创建了封装服务接口的代理,并使用自身作为拦截器类,又因为实现了FactoryBean接口,所以获取Bean的时候实际上获取的是创建的代理,而在调用相应方法时实际调用的是代理类中的服务方法,所以将会使用增强器进行增强,所以现在谈到的问题就是调用的问题,HttpInvokerProxyFactoryBean的invoke的逻辑如下:
(1)构建RemoteInvocation实例,因为是代理中增强方法的调用,所以当进入invoke方法时,实际上MethodInvocation中已经包含了调用的接口的相关信息,所以我们首先需要将这一部分的消息提取出来,并构建RemoteInvocation实例
(2)远程执行方法该逻辑比较复杂,下面是它的执行步骤:
1.将调用方法的实例写入输出流中
2.创建HttpPost,因为对服务端方法的调用是通过Post方法进行的,所以需要先创建HttpPost
3.设置RequestBody并将序列化流加入到postMethod中
4.执行远程方法,通过HttpClient所提供的方式来直接执行远程方法
5.根据返回的响应码进行验证
6.提取返回的输入流对于信息的处理可能存在差别,不同的方式采用不同的办法进行提取
7.提取返回结果
(3)提取结果从服务器端的实现可知,实际上返回的结果类型为RemoteInvocationResult,所以我们还需要对结果类型进行拆封

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值