Spring远程调用HttpInvoker
Spring支持的远程调用方式有RMI、Caucho的Hessian和Burlap,使用SOAP和JAX-RPC的Web Services,以及Spring自己的Http invoker。
RMI需要客户端、服务器都使用Java,使用标准的java序列化机制,但是容易受到网络限制(如防火墙)。Hessian或Burlap通过HTTP协议访问,但是序列化不是java的序列化机制。Spring自己的Http invoker则融合了RMI和Hessian的优点,能通过HTTP访问,不受防火墙限制,同时使用标准的java序列化机制,但是需要Spring框架的支持。
1、Spring对RMI的支持
如果从头写RMI需要处理RemoteException异常,使用rmic编译程序,生成客户端代理和服务器框架,涉及如下步骤:
(1)、编写服务实现类,类中的方法能够抛出java.rmi.RemoteException。
(2)、创建服务接口,以便扩展java.rmi.Remote。
(3)、运行RMI编译器(rmic),创建客户端stub类和服务器端skeleton类。
(4)、启动一个RMI注册,在RMI注册表中注册服务。
而使用Spring节省了很多不必要的代码,不需要抛出RemoteException,只需简单地编写能实现服务功能的POJO,Spring会处理其他事情。使用Spring的RmiServiceExporter可以把任何Spring管理的Bean输出成一个RMI服务,如下图:
RmiServiceExporter通过把POJO置入服务适配器并将服务适配器与RMI注册表绑定使POJO转换成RMI服务。
2、Spring对Hassian和Burlap的支持
Hassian和Burlap是Caucho Technology提供的两种解决方法,是基于HTTP的轻量级远程服务。Hessian像RMI那样,使用二进制消息来建立客户端和服务端之间的交流。Burlap是一种基于XML的远程技术。Spring提供了HessianProxyFactoryBean和BurlapProxyFactoryBean来生成代理对象负责通过HTTP与远程服务通信,如下图:
3、Spring Http Invoker
Spring的远程调用机制HttpInvoker是一个新的远程调用模型,作为Spring框架的一部分,使用Java的序列化机制,执行基于HTTP的远程调用(不受防火墙的影响)。
通过一个简单例子来看一下HttpInvoker的使用,假设提供一个简单的helloService,要将这个服务公开,需要使用HttpInvokerServiceExporter来将helloService公开,服务接口为Hello,而服务的实现类为HelloImpl。
服务接口很简单:
public interface Hello {
public Person hello(String name);
}
要传递给客户端的Person类,需要实现序列化接口:
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
服务实现类简单的创建一个Person对象然后返回给客户端:
public class HelloImpl implements Hello {
public Person hello(String name) {
Person p = new Person();
p.setAge(10);
p.setName(name);
return p;
}
}
HttpInvokerServiceExporter实际上是一个spring mvc控制器,它处理客户端的请求并调用服务实现。Service及实现类配置在applicationContext.xml文件中,如下:
<bean id="httpService"
class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service">
<ref bean="helloService" />
</property>
<property name="serviceInterface" value="httpinvoker.Hello"></property>
</bean>
<bean id="helloService" class="httpinvoker.HelloImpl" />
Web.xml配置spring servlet入口
<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">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>service</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>service</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
</web-app>
service-servlet.xml中配置spring url处理器,这里使用SimpleUrlHandlerMapping:
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/httpService">httpService</prop>
</props>
</property>
</bean>
客户端使用相同的接口Hello及Person类,使用HttpInvokerProxyFactoryBean创建代理bean,设置属性serviceUrl为服务器的服务访问url:
<bean id="httpService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl">
<value>http://localhost:8080/hello/service/httpService</value>
</property>
<property name="serviceInterface" value="httpinvoker.Hello"/>
</bean>
客户端可以使用一个简单的测试程序测试服务器的服务:
ApplicationContext appContext = new FileSystemXmlApplicationContext(
"classpath:application-client.xml");
Hello helloService = (Hello) appContext.getBean("httpService");
System.out.println("helloService is " + helloService);
Person person = helloService.hello("df");
System.out.println("name: " + person.getName() + ", age: "
+ person.getAge());