当前位置: 移动技术网 > IT编程>开发语言>Java > Spring HttpInvoker 从实战到源码追溯

Spring HttpInvoker 从实战到源码追溯

2018年10月31日  | 移动技术网IT编程  | 我要评论

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

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

   http 远程调用框架不是有成熟的 hessian、burlap嘛,spring 团队为什么还要重复造轮子呢? 

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

1.项目实战

   

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

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

   接口定义如下:

public interface userservice {


    /**
     * 通过id获取用户
     *
     * @param uuid 用户id
     * @return 用户实体
     */
    user getuserbyid(string uuid);
}

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

public class userserviceimpl implements userservice {

    @override
    public user getuserbyid(string uuid) {
        user user = new user();
        user.setuuid(uuid);
        user.setname("orson");
        user.setpasswd("xyxy");
        user.setsex("f");
        user.setphone("13974856211");
        user.setphoto("/photo/user/xyxy.gif");
        user.setemail("954875698@qq.com");
        user.setcreateby("orson");
        return user;
    }
}

   spring 配置服务如下:

    <bean id="userserviceimpl" class="com.rambo.httpinvoker.server.impl.userserviceimpl" />

    <bean id="userserviceinvoker" class="org.springframework.remoting.httpinvoker.httpinvokerserviceexporter">
        <property name="service" ref="userserviceimpl" />
        <property name="serviceinterface" value="com.rambo.httpinvoker.api.userservice" />
    </bean>

    <bean class="org.springframework.web.servlet.handler.simpleurlhandlermapping">
        <property name="mappings">
            <props>
                <prop key="/userservice">userserviceinvoker</prop>
            </props>
        </property>
    </bean>

   web.xml 配置:

    <servlet>
        <servlet-name>service</servlet-name>
        <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class>
        <init-param>
            <param-name>contextconfiglocation</param-name>
            <param-value>classpath:spring-httpinvoke-server.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>service</servlet-name>
        <url-pattern>/service/*</url-pattern>
    </servlet-mapping>

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

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

    <!-- 客户端使用 httpinvokerproxyfactorybean 代理客户端向服务器端发送请求,请求接口为 userservice 的服务 -->
    <bean id="userservice" class="org.springframework.remoting.httpinvoker.httpinvokerproxyfactorybean">
        <property name="serviceurl" value="http://${server.url}/service/userservice"/>
        <property name="serviceinterface" value="com.rambo.httpinvoker.api.userservice"/>
    </bean>

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

2.源码分析

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

   httpinvokerserviceexporter 继承 httprequesthandler 并实现 handlerequest 方法。

    public void handlerequest(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        try {
            remoteinvocation invocation = this.readremoteinvocation(request);
            remoteinvocationresult result = this.invokeandcreateresult(invocation, this.getproxy());
            this.writeremoteinvocationresult(request, response, result);
        } catch (classnotfoundexception var5) {
            throw new nestedservletexception("class not found during deserialization", var5);
        }
    }

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

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

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

    public void afterpropertiesset() {
        super.afterpropertiesset();
        if (this.getserviceinterface() == null) {
            throw new illegalargumentexception("property 'serviceinterface' is required");
        } else {
            this.serviceproxy = (new proxyfactory(this.getserviceinterface(), this)).getproxy(this.getbeanclassloader());
        }
    }

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

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

   追溯这几个过程的时候你会看到,cgb 代理拦截器 methodinterceptor、java 序列对象 objectoutputstream、java http 连接对象 httpurlconnection。

   httpinvoker 调优时也记得去关注上述几个对象:

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

    public object invoke(methodinvocation methodinvocation) throws throwable {
        if (aoputils.istostringmethod(methodinvocation.getmethod())) {
            return "http invoker proxy for service url [" + this.getserviceurl() + "]";
        } else {
            remoteinvocation invocation = this.createremoteinvocation(methodinvocation);

            remoteinvocationresult result;
            try {
                result = this.executerequest(invocation, methodinvocation);
            } catch (throwable var7) {
                remoteaccessexception rae = this.converthttpinvokeraccessexception(var7);
                throw (throwable)(rae != null ? rae : var7);
            }
            return this.recreateremoteinvocationresult(result);
        }
    }

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网