Spring整合Hessian与分析

原创
2017/06/27 22:00
阅读数 3.5K

前言
上一篇文章Hessian入门体验与分析介绍了hessian的简单入门,并且从源码层面对Hessian的调用流程进行了分析;发现使用原生的Hessian还是比较繁琐的,下面看看Spring与Hessian进行整合并且进行简要分析。

使用
提供三个模拟块,分别模拟client,server以及被依赖的jar;对应的模块名称分别是:hessianClient,hessianServer以及hessianJar
1.hessianJar介绍
hessianJar主要提供被hessianClient和hessianServer依赖的公共类,这里主要提供了接口类IHessianService和pojo对象Bean
IHessianService类:

public interface IHessianService {
      
    public String getString(String value);
      
    public Bean getBean();
}

对象Bean:

public class Bean implements Serializable {
    private static final long serialVersionUID = 1L;
    private String value;
  
    public Bean(String value) {
        this.value = value;
    }
  
    public String getValue() {
        return value;
    }
  
    public void setValue(String value) {
        this.value = value;
    }
}

2.hessianSever介绍
hessianServer主要用来对外提供服务的,因为hessian本身是基于http协议的,所以可以直接部署到web容器中比如tomcat,http协议解析可以直接交给web容器;此处因为将服务的发布交给Spring来处理,提供配置文件如下:
spring-server-hessian.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" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
 
    <bean id="hessionService" class="zh.hessian.hessianServer.HessianServiceImpl" />  
     
    <bean name="/hessianService.do"
        class="org.springframework.remoting.caucho.HessianServiceExporter">
        <property name="service" ref="hessionService" />
        <property name="serviceInterface" value="zh.hessian.hessianJar.IHessianService" />
    </bean>
</beans> 

web.xml:

<web-app>
  <display-name>Archetype Created Web Application</display-name>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath:spring-hessian.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

HessianServiceImpl:

public class HessianServiceImpl implements IHessianService {
 
    @Override
    public String getString() {
        return "string";
    }
 
    @Override
    public Bean getBean() {
        return new Bean("value");
    }
}

3.hessianClient介绍
hessianClient模拟客户端的调用,对hessianSever发起请求,并且接受回复;此处因为将客户端的调用交给Spring来处理,提供配置文件如下:
spring-client-hessian.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" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
    <bean id="hessionServiceClient"
        class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <property name="serviceUrl"
            value="http://localhost:8080/hessianServer/hessianService.do" />
        <property name="serviceInterface" value="zh.hessian.hessianJar.IHessianService" />
    </bean>
</beans>  

HessianSpringClient:

public class HessianSpringClient {
 
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-client-hessian.xml");
        IHessianService service = (IHessianService) context.getBean("hessionServiceClient");
        System.out.println(service.getString());
        System.out.println(service.getBean().getValue());
    }
}

部署测试
运行结果如下:

getString:REQ + zhaohui
getBean:value

Spring整合Hessian调用分析
1.HessianProxyFactoryBean类
配置文件spring-client-hessian.xml中定义的对象class都是HessianProxyFactoryBean类,而我们通过上一篇文章中了解到Hessian通过在客户端使用动态代理的方式来实现RPC,HessianProxyFactoryBean代码如下:

public class HessianProxyFactoryBean extends HessianClientInterceptor implements FactoryBean<Object> {
 
    private Object serviceProxy;
 
    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        this.serviceProxy = new ProxyFactory(getServiceInterface(), this).getProxy(getBeanClassLoader());
    }
 
    public Object getObject() {
        return this.serviceProxy;
    }
 
    public Class<?> getObjectType() {
        return getServiceInterface();
    }
 
    public boolean isSingleton() {
        return true;
    }
}

发现HessianProxyFactoryBean实现了FactoryBean接口,而接口的方法getObject()正是我们在调用context.getBean(“x”)的时候被调用,所以获取的bean其实是serviceProxy,通过ProxyFactory的getProxy方法获取,具体代码如下:

public Object getProxy(ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

serviceProxy其实是通过AopProxy获取的代理类,AopProxy有两个实现类分别是:CglibAopProxy和JdkDynamicAopProxy,分别对应的两种动态代理方式,具体使用哪种,通过DefaultAopProxyFactory中如下代码来实现:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) 
    {
        Class targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface()) {
            return new JdkDynamicAopProxy(config);
        }
        return CglibProxyFactory.createCglibProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

这里使用的是JdkDynamicAopProxy,部分代码如下:

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + 
                              this.advised.getTargetSource());
        }
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
 
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                ......
                Object retVal;
                ......
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                retVal = invocation.proceed();
                ......
                return retVal;
        }
 
}

JdkDynamicAopProxy实现了InvocationHandler接口,每次在调用方法(如:hessianService.getString(“zhaohui”))时,自动触发invoke方法;这里将需要的参数比如:(target,method,args等)封装到了ReflectiveMethodInvocation中,在ReflectiveMethodInvocation的proceed()方法中又调用了HessianProxyFactoryBean中的invoke()方法,部分代码如下:

public Object invoke(MethodInvocation invocation) throws Throwable {
        if (this.hessianProxy == null) {
            throw new IllegalStateException("HessianClientInterceptor is not properly initialized - " +
                    "invoke 'prepare' before attempting any operations");
        }
 
        ClassLoader originalClassLoader = overrideThreadContextClassLoader();
        try {
            return invocation.getMethod().invoke(this.hessianProxy, invocation.getArguments());
        }
        ......
}

其中最关注的是hessianProxy对象,这里通过反射的方式调用了hessianProxy对象里面的指定方法(比如:hessianService.getString(“zhaohui”)),hessianProxy对象在初始化HessianProxyFactoryBean的时候就初始化好了,具体代码如下:

protected Object createHessianProxy(HessianProxyFactory proxyFactory) throws MalformedURLException {
    Assert.notNull(getServiceInterface(), "'serviceInterface' is required");
    return proxyFactory.create(getServiceInterface(), getServiceUrl());
}

这段代码有没有很熟悉,就是上一篇文章Hessian入门体验与分析中的客户端代码,如下所示:

String url = "http://localhost:8080/hessianServer-0.0.1-SNAPSHOT/hessianService";
HessianProxyFactory factory = new HessianProxyFactory();
IHessianService hessianService = null;
hessianService = (IHessianService) factory.create(IHessianService.class, url);

这里的getServiceInterface()和getServiceUrl()正是我们在spring-client-hessian.xml为hessionServiceClient配置的两个属性,其实到这里下面的流程就和上一篇文章Hessian入门体验与分析中完全一样了。

2.代理类HessianProxy
具体分析同Hessian入门体验与分析

3.http请求类
具体分析同Hessian入门体验与分析

4.发送请求
具体分析同Hessian入门体验与分析

5.服务器端接受消息
web.xml中配置了处理消息的servlet:DispatcherServlet,在启动服务器并初始化DispatcherServlet的时候,加载了配置在spring-server-hessian.xml中的org.springframework.remoting.caucho.HessianServiceExporter;DispatcherServlet只是启动一个映射的作用,真正的处理在
HessianServiceExporter类中,部分代码如下:

public void invoke(InputStream inputStream, OutputStream outputStream) throws Throwable {
    Assert.notNull(this.skeleton, "Hessian exporter has not been initialized");
    doInvoke(this.skeleton, inputStream, outputStream);
}
 
protected void doInvoke(HessianSkeleton skeleton, InputStream inputStream, OutputStream outputStream)
        throws Throwable {
        ......
        AbstractHessianInput in;
        AbstractHessianOutput out;
 
        if (code == 'H') {
            // Hessian 2.0 stream
            major = isToUse.read();
            minor = isToUse.read();
            if (major != 0x02) {
                throw new IOException("Version " + major + "." + minor + " is not understood");
            }
            in = new Hessian2Input(isToUse);
            out = new Hessian2Output(osToUse);
            in.readCall();
        }
        else if (code == 'C') {
            // Hessian 2.0 call... for some reason not handled in HessianServlet!
            isToUse.reset();
            in = new Hessian2Input(isToUse);
            out = new Hessian2Output(osToUse);
            in.readCall();
        }
        else if (code == 'c') {
            // Hessian 1.0 call
            major = isToUse.read();
            minor = isToUse.read();
            in = new HessianInput(isToUse);
            if (major >= 2) {
                out = new Hessian2Output(osToUse);
            }
            else {
                out = new HessianOutput(osToUse);
            }
        }
        ......
        skeleton.invoke(in, out);
        ......
}

HessianServiceExporter在实例化的同时也初始化了HessianSkeleton对象;又进一步的将Inputstream封装入HessianInput中,将Outputstream封装入Hessian2Output中;接下来把HessianInput和Hessian2Output传入HessianSkeleton中,消息的读取和回复都交给HessianSkeleton来处理;后面的处理具体分析同Hessian入门体验与分析中介绍的。

5.Client接受服务器的回复
具体分析同Hessian入门体验与分析

总结
本文通过hessianJar,hessianClient已经hessianServer三个模块,提供了Spring整合Hessian的实例;通过与Spring的整合,简化了开发;然后从代码层面将包裹在Hessian外层的Spring剥离,还原原始的Hessian调用。

个人博客:codingo.xyz

展开阅读全文
加载中
点击加入讨论🔥(1) 发布并加入讨论🔥
1 评论
26 收藏
1
分享
返回顶部
顶部