Spring Integrates Hessian and Analysis

Posted by deadonarrival on Tue, 18 Jun 2019 19:05:47 +0200

Preface
Last article Hessian's Beginning Experience and Analysis Introduces a simple introduction to hessian, and analyzes Hessian's call flow from the source level. It is found that using native Hessian is more cumbersome. Here's a look at Spring's integration with Hessian and a brief analysis.

Use
Provides three simulation blocks to simulate client, server, and dependent jar; corresponding module names are hessianClient, hessianServer, and hessianJar
1. Introduction to Hessian Jar
hessianJar mainly provides public classes that are dependent on by hessianClient and hessianServer, where it mainly provides interface classes IHessianService and pojo object Bean
IHessianService class:

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

Object 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. Introduction to hessianSever
hessian Server is mainly used to provide external services, because hessian itself is based on http protocol, so it can be deployed directly to web containers such as tomcat, http protocol resolution can be handed directly to web containers; here, because publishing services is handled by Spring, the following configuration files are provided:
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. Introduction to hessianClient
The hessianClient simulates a client's call, makes a request to hessianSever, and accepts a reply; here, because the client's call is handed over to Spring, a configuration file is provided as follows:
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());
    }
}

Deployment Tests
The results are as follows:

getString:REQ + zhaohui
getBean:value

Spring Integrated Hessian Call Analysis
1.HessianProxyFactoryBean class
The object classes defined in the configuration file spring-client-hessian.xml are all HessianProxyFactoryBean classes, and we learned in the previous article that Hessian implements RPC by using a dynamic proxy on the client side. The HessianProxyFactoryBean code is as follows:

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;
    }
}

We found that HessianProxyFactoryBean implements the FactoryBean interface, and the interface method getObject() is called exactly when we call context.getBean("x"), so the obtained bean is actually a serviceProxy, which is obtained through the ProxyFactory getProxy method with the following code:

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

Service Proxy is actually a proxy class obtained through AopProxy. There are two implementation classes for AopProxy: CglibAopProxy and JdkDynamicAopProxy. They are two corresponding dynamic proxy methods, which one to use, through the following code in 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 is used here, with some code as follows:

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 implements the InvocationHandler interface and automatically triggers the invoke method each time a method is invoked (such as hessianService.getString("zhaohui"); the required parameters such as: (target,method,args, etc.) are encapsulated in the ReflectiveMethodInvocation, and invo in the ReflectiveMethodInvocation proceed() method is invoked in the HessianProxyFactoryBean.Ke() method, with some code as follows:

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());
        }
        ......
}

One of the most interesting is the hessianProxy object, where the specified method inside the hessianProxy object is invoked by reflection (for example, hessianService.getString("zhaohui"). The hessianProxy object is initialized when it initializes the HessianProxyFactoryBean, as follows:

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

Is this code familiar with the last article? Hessian's Beginning Experience and Analysis Client code in as follows:

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);

Here, getServiceInterface() and getServiceUrl() are the two properties that we configured for the hessionServiceClient in spring-client-hessian.xml, and the process below is actually the same as the previous article Hessian's Beginning Experience and Analysis It's exactly the same.

2. Proxy class HessianProxy
Specific analysis is the same Hessian's Beginning Experience and Analysis

3.http Request Class
Specific analysis is the same Hessian's Beginning Experience and Analysis

4. Send Request
Specific analysis is the same Hessian's Beginning Experience and Analysis

5. Server-side acceptance of messages
The web.xml is configured with a message-handling servlet: Dispatcher Servlet, which loads the org.springframework.remoting.caucho.HessianServiceExporter configured in spring-server-hessian.xml when the server is started and the Dispatcher Servlet is initialized; Dispatcher Servlet just starts a mapping, and the real processing takes place in spring-server-hessian.xml
Part of the code in the HessianServiceExporter class is as follows:

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 initializes the HessianSkeleton object as well as instantiates it; further encapsulates Inputstream into HessianInput and Outputstream into Hessian2Output; next, HessianInput and Hessian2Output are passed into HessianSkeleton, and both the reading and reply of the message are handled by HessianSkeleton; the subsequent analysis is the same as that of HessianSkeleton. Hessian's Beginning Experience and Analysis Described in.

5.Client accepts a response from the server
Specific analysis is the same Hessian's Beginning Experience and Analysis

summary
In this paper, through Hessian Jar, Hessian Client has provided three modules of Hessian Server, providing an instance of Spring's integration with Hessian; simplifying development by integrating with Spring; and then stripping out the Spring wrapped in the outer layer of Hessian from the code level to restore the original Hessian call.

Topics: Spring xml encoding Tomcat