最近在做项目时用到了很多远程方法调用,如Hession、RMI、EJB、JMS等,感觉Spring对这些的包装真是不不错,小记一下。
一、Hession:
相比WebService,Hession更简单、快捷。采用二进制RPC协议,由于采用的是二进制协议,所以它很适合于发送二进制数据。
Hession通过Servlet来提供远程服务。下面介绍两种Hession服务的发布和调用方法。
1、Servlet采用Hession自带的HessionServlet来提供服务,通过HessionProxyFactory工具类来调用Hession服务。
web.xml配置如下:
....
<servlet>
<servlet-name>hessionService</servlet-name>
<servlet-class>com.caucho.hessian.server.HessionServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>service-class</param-name>
<param-value>lee.HelloImpl</param-value>
</init-param>
</servlet
<servlet-maping>
<servlet-name>hessionService</servlet-name>
<url-patter>/hessionService</url-pattern>
</servlet-mapping>
.............
客户端调用:
..............
String url = "http://localhost:8888/hession/hessionService";
HessionPorxyFactory factory = new HessionPorxyFactory();
//获得Hession服务的远程调用
Hello h = (Hello) factory.create(Hello.class,url);
//调用远程服务
System.out.println(d.hello("xixi"));
..............
2、servlet采用Spring的DispatcherServlet来提供服务,客户端采用Spring的HessionProxyFactoryBean连接Hession服务。
首先需要在web.xml中配置DispatchetServlet。
其次建立[servlet-name]-servlet.xml文件,该方件用于配置对外提供的服务,这里要用到Spring框架提供的HessianServiceExporter来定义输出的服务。它的功能就是把普通Bean定义可对外提供服务的Bean。这是由于DispatchetServlet查找提供服务的Bean时,是通过与配置的Servlet名字相对应的[servlet-name]-servlet.xml,
客户端采用HessionProxyFactoryBean主要是利用Spring的IOC,否则的话完全可以用第一种方法。在客户端的Spring配置文件中,用HessionProxyFactoryBean把远程提供服务的Bean(对应上面servletname-servlet.xml中配置的输出服务bean)配置到本地BeanFactory,使用时如同使用本地Bean方法一样。
二、HttpInvoker:
使用 HttpInvoker,不需要额外的类库,与Hession的轻量级传输协议不同,Spring HttpInvoker使用Java序列化来序列化参数和返回值,然后基于Http协议传输经序列化后的对象,当参数或返回值是复杂类型,并且不能通过Hession的序列化机制序列化时,HttpInvoker就很有优的势。
它的用法与Hession非常相似。Spring使用HttpInvokerServiceExporter把普通bean实例输出成远程对象。客户端连接Spring提供了HttpInvokerProxyFactoryBean工厂连接服务,类似于HessionProxyFactoryBean,配置时只需指定服务URL和服务实现接口,通过代理,Spring可将调用转换成POST请求发送到指定服务。
三、RMI:
客户端和服务器端必须是纯Java实现。RMI服务是典型的面向接口编程,只有在远程接口里定义的方法才会作为远程服务,远程方法的返回值和参数都必须实现Serializable接口,因为远程在网络上传输只能传输字节流,因此,要求参数、返回值都可以转换成字节流-即实现序列化。
一、传统使用方法:
1、远程服务提供类必须实现远程接口(java.rmi.Remote)并继承java.rmi.server.UnicastRemoteObject对象。远程服务类必须有构造器,而且构造器必须抛出RemoteException异常。
2、注册服务:
Server imp = new ServerImpl(); //创建远程服务类实例
LocateRegistry.createRegistry(1099); //注册远程服务的端口
Naming.rebind("rmi://:1099/fdf",imp); //将远程服务实例绑定为远程服务
3、对于使用RMI,将源文件存盘编译还不够,还必须使用rmic命令编译服务类,编译服务类是为了行成stub和skeleton。
4、客户端要面向接口编程,客户端部分需要服务实现接口的.class文件和生成的stub文件。客户端调用示例代码如下:
public class RMIClient {
public static void main(String[] args) throws Exception {
Server ser = (Server)Naming.lookup("rmi://:1099/fdf");
System.out.println(ser.helloWorld("yeeku"));
System.out.println(ser.getPerson("yeeku",28));
}
}
原理:RMI的具体实现,依然是依赖于底层的Socket编程。RMI依赖于TCP/IP传输协议,服务器端skeleton建立ServerSocket监听请求,而客户端建立Socket请求边接。RMI实现网络传输的多线程、IO等底层细节。这些细节的实现就隐藏在rmic命令的执行中,使用rmic命令编译时生成如下两个class文件:
stub:该文件用于与客户端交流,建立Socket请求连接。
skeleton:该文件用于与服务端门交流,建立ServerSocket监听请求。
二、Spring封装
1、服务器端的接口不用实现Remote接口,实现类也不用继承UnicastRemoteObject类,都是普通的Java接口和类。
2、如果要暴露远程方法,Spring提供了RmiServiceExporter类,该类可以将一个普通Bean实例绑定成远程服务。将普通bean实例绑定为远程服务的完整配置如下:
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- 指定暴露出来的远程服务名,可任意取名字,但客户端要用到该名字-->
<property name="serviceName">
<value>RealtimeTransferService</value>
</property>
<!--配置暴露目标-->
<property name="service" ref="realtimeTransfer" />
<!--配置bean实现的接口,该接口被当作远程接口对待-->
<property name="serviceInterface"
value="com.topnet.tais.prefixmachine.business.realtimepay.RealtimeTransfer" />
<!--指定RMI远程服务的端口号-->
<property name="registryPort">
<value>1099</value>
</property>
3、客户端访问方式。
<bean id = "" class= "org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl">
<value>rmi://1227.0.0.1:1099/RealtimeTransferService</value>
</property>
<property name="serviceInterface">
<value>com.topnet.tais.prefixmachine.business.realtimepay.RealtimeTransfer</value>
</property>
</bean>
四、JMS:
JMS主要有两个版本:1.0.2和1.1,二者的区别:JMS1.0.2对两种消息模型提供了不同的类体系。JMS1.1则使用统一模型的概念,从而减少两种模型之间的差别,避免客户端代码的差别。
消息的两种模型:
点对点消息处理:这种消息处理模型为应用中的各个逻辑处理单元提供可靠的通信支持,JMS系统保证消息传递给消息接收者,不会同被多个接收者接收,如果消息接收者暂不在连接范围内,JMS保证消息不会丢失,直到接收者进入连接,消息将自动送达。因此,JMS将消息保存到永久性介质,如数据库或文件上。
发布/订阅消息处理:
使用这种模型,可以将消息发送到一个主题,每个子主题可以有多个订阅者。JMS系统负责将消息的副本传给主题的每个订阅者。
JMS开发:
发送步骤:1、连接工厂创建JMS连接;2、JMS连接创建JMS会话;3、JMS会话创建消息生产者;4、JMS会话创建空JMS消息;5、JMS消息调用自身的主法填充内容;6、JMS消息生产者发送消息。
接收步骤:1、连接工厂创建JMS连接;2、JMS连接创建JMS会话;3、JMS会话创建JMS消费者;4、JMS消费者接收消息,同步和异步接收消息方工略有差异。
Spring对JMS的支持:
1、提供了JmsTemplate
2、管理连接工厂,Spring提供了一个ConnectionFactory的实现SingleConnectionFactory,该连接工厂对所有的createConnection调用返回同一个连接,并忽略close的调用。这在测试和独立的环境中相当有用,只有同一个连接被用于多个JmsTemplate,这样才可以跨越多个事务。创建SingleConnectionFactory必须提供一个标准ConnectionFactory的引用,作为目标连接工厂,目票连接工厂需要应用服务器提供。
3、管理消息目的,消息生产者的消息目的也是消息消费提取消息的消息源,当配置Spring应用上下文时,可以使用工厂类 JndiObjectFactory配置消息目的。布尔属性PubSubDomain用来配置JmsTemplate是否使用Pub/Sub模型,默认值是false,消息队列模型。该属性对JMS1.1没有影响。DefaultDestionation属性用于配置JmsTemplate的默认目的,如果配置了默认目的,JmsTemplate发送和接收操作可以无须指定消息目的。
4、JMS与事务,Spring提供一个JmsTransactionManager来管理事务,它对于SingleConnectionFactory有效。JmsTransactionManger将Connection/Session对绑定到线程,然而在一个J2EE环境中,ConnectionFactory将缓存连接和会话,所以被绑定到线程的实例依赖于缓存行为。JmsTempate也能和JtaTransactionManager一起使用,以完成分布式事务。
Spring中的配置:
1、JNDI上下文是取得JMS资源的起始位置,因此首先要配置JNDI模板,如下所示:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<description>jndi环境配置</description>
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
</props>
</property>
</bean>
2、配置队列连接工厂
<bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<description>jms连接工厂</description>
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>Toptais3Ext-JMSConnectionFactory</value>
</property>
</bean>
3、配置消息目的
<bean id="destinationBatchStartQueue" class="org.springframework.jndi.JndiObjectFactoryBean">
<description>页面发起批量代扣请求的长事件Queue </description>
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>Toptais3Ext-BatchStartQueue</value>
</property>
</bean>
4、配置JmsTemplate
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate102">
<description>发送JMS消息的模板</description>
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.SingleConnectionFactory102">
<property name="targetConnectionFactory" ref="jmsConnectionFactory"/>
</bean>
</property>
<property name="messageConverter">
<ref bean="messageConverter"/>
</property>
</bean>
5、配置发送和接收bean
五、EJB:
六、WebService: