一、介绍
目前,Spring提供对下面四种远程访问技术的支持:
- 远程方法调用(RMI):通过使用RmiProxyFactoryBean和RmiServiceExporter,Spring支持传统的RMI(使用java.rmi.Remote interfaces 和 java.rmi.RemoteException)和通过RMI调用器(可以使用任何Java接口)的透明远程调用。
- Spring的HTTP调用器:Spring提供一种特殊的远程调用策略支持任何Java接口(象RMI调用器一样),它允许Java序列化能够通过HTTP传送。对应的支持类是HttpInvokerProxyFactoryBean和HttpInvokerServiceExporter。 和Burlap和Hessian使用自身序列化机制的轻量级协议相反,Spring HTTP调用器使用标准Java序列化机制来通过HTTP输出业务。如果你的参数或返回值是复杂类型,并且不能通过Hessian和Burlap的序列化机制序列化,HTTP调用器就很有优势。Spring可以使用J2SE提供的标准功能或Commons的HttpClient来实现HTTP调用。如果你需要更先进,更好用的功能,就使用后者。
- Hessian:通过使用HessianProxyFactoryBean和HessianServiceExporter,你可以使用Caucho提供的轻量级基于HTTP的二进制协议透明地提供你的业务。Hessian提供了一个基于HTTP的二进制远程协议。它由Caucho创建。
- Burlap:Burlap是基于XML的,它可以完全代替Hessian。Spring提供的支持类有BurlapProxyFactoryBean和BurlapServiceExporter。 Burlap是Hessian的基于XML实现。
二、如何选择?
当使用RMI时,通过HTTP协议访问对象是不可能的,除非你用HTTP包裹RMI流。RMI是一种很重的协议,因为他支持完全的对象序列化,这样的序列化在要求复杂数据结构在远程传输时是非常重要的。然而,RMI-JRMP只能绑定到Java客户端:它是一种Java-to-Java的远程访问的方案。
如果你需要基于HTTP的远程访问而且还要求使用Java序列化,Spring的HTTP调用器是一个很好的选择。它和RMI调用器使用相同的基础设施,仅仅使用HTTP作为传输方式。注意HTTP调用器不仅只能用在Java-to-Java的远程访问,而且在客户端和服务器端都必须使用Spring。(Spring为非RMI接口提供的RMI调用器也要求客户端和服务器端都使用Spring)
当在异构环境中,Hessian和Burlap就非常有用了。因为它们可以使用在非Java的客户端。然而,对非Java支持仍然是有限制的。已知的问题包括含有延迟初始化的collection对象的Hibernate对象的序列化。如果你有一个这样的数据结构,考虑使用RMI或HTTP调用器,而不是Hessian。
最后但也很重要的一点,EJB优于RMI,因为它支持标准的基于角色的认证和授权,以及远程事务传递。用RMI调用器或HTTP调用器来支持安全上下文的传递是可能的,虽然这不是由核心Spring提供:而是由第三方或在定制的解决方案中插入拦截器来解决的。
三、例子
定义好用于测试的接口和实现。
package com.logcd.server.service; public interface IHelloService { public String doHello(String name); }
package com.logcd.server.service.impl; import com.logcd.server.service.IHelloService; public class HelloService implements IHelloService{ public String doHello(String name) { return "Hello , " + name; } }
(一)使用Hessian
(1)server端:web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>Hessian</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:Hessian-servlet.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Hessian</servlet-name> <url-pattern>/hessian/*</url-pattern> </servlet-mapping> </web-app>
(2)server端:Hessian-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="helloService" class="com.logcd.server.service.impl.HelloService"/> <bean name="/helloService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="helloService"/> <property name="serviceInterface"> <value> com.logcd.server.service.IHelloService </value> </property> </bean> </beans>
(3)client端测试:Hessian-client.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="helloService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl"> <value>http://localhost:8080/hessian/helloService</value> </property> <property name="serviceInterface"> <value>com.logcd.server.service.IHelloService</value> </property> </bean> </beans>
(4)测试程序:
package com.logcd.client.test; import java.net.MalformedURLException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.caucho.hessian.client.HessianProxyFactory; import com.logcd.server.service.IHelloService; public class HessianTest { public static void main(String args[]){ clientSpringTest(); } public static void clientSpringTest(){ ApplicationContext context= new ClassPathXmlApplicationContext("Hessian-client.xml"); IHelloService helloService = (IHelloService)context.getBean("helloService"); System.out.println(helloService.doHello("logcd")); } public static void clientJavaTest(){ String url = "http://localhost:8080/hessian/helloService"; HessianProxyFactory factory = new HessianProxyFactory(); try { IHelloService helloService =(IHelloService)factory.create(IHelloService.class, url); System.out.println(helloService.doHello("logcd")); } catch (MalformedURLException e) { e.printStackTrace(); } } }
(二)使用HTTP调用器
(1)server端:web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>httpInvoker</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:httpinvoker-servlet.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>httpInvoker</servlet-name> <url-pattern>/httpInvoker/*</url-pattern> </servlet-mapping> </web-app>
(2)server端:httpinvoker-servlet.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="helloService" class="com.logcd.server.service.impl.HelloService"/> <bean name="/helloService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" lazy-init="false"> <property name="service"> <ref bean="helloService"/> </property> <property name="serviceInterface"> <value>com.logcd.server.service.IHelloService</value> </property> </bean> </beans>
(3)client端:httpinvoker-client.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="helloService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <property name="serviceUrl"> <value>http://localhost:8080/httpInvoker/helloService</value> </property> <property name="serviceInterface"> <value>com.logcd.server.service.IHelloService</value> </property> <!-- 默认情况下,客户端的HttpInvokerProxy使用J2SE的HTTP Client来建立连接 org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor --> <property name="httpInvokerRequestExecutor"> <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor" /> </property> </bean>
(4)测试类
package com.logcd.client.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean; import com.logcd.server.service.IHelloService; public class HttpInvokerTest { public static void main(String args[]){ clientJavaTest(); } public static void clientSpringTest(){ ApplicationContext context= new ClassPathXmlApplicationContext("httpinvoker-client.xml"); IHelloService helloService = (IHelloService)context.getBean("helloService"); System.out.println(helloService.doHello("logcd")); } public static void clientJavaTest(){ String url = "http://localhost:8080/httpInvoker/helloService"; HttpInvokerProxyFactoryBean factory = new HttpInvokerProxyFactoryBean (); factory.setServiceInterface(IHelloService.class); factory.setServiceUrl(url); factory.afterPropertiesSet(); IHelloService helloService = (IHelloService)factory.getObject(); System.out.println(helloService.doHello("logcd")); } }
四、使用自定义Annotation实现
(1)Custom Annotation Configuration for Spring Remoting
(2)Custom Annotation Configuration for Spring Remoting, Part Two