最近在做項(xiàng)目時(shí)用到了很多遠(yuǎn)程方法調(diào)用,如Hession、RMI、EJB、JMS等,感覺(jué)Spring對(duì)這些的包裝真是不不錯(cuò),小記一下。
一、Hession:
相比WebService,Hession更簡(jiǎn)單、快捷。采用二進(jìn)制RPC協(xié)議,由于采用的是二進(jìn)制協(xié)議,所以它很適合于發(fā)送二進(jìn)制數(shù)據(jù)。
Hession通過(guò)Servlet來(lái)提供遠(yuǎn)程服務(wù)。下面介紹兩種Hession服務(wù)的發(fā)布和調(diào)用方法。
1、Servlet采用Hession自帶的HessionServlet來(lái)提供服務(wù),通過(guò)HessionProxyFactory工具類(lèi)來(lái)調(diào)用Hession服務(wù)。
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>
.............
客戶(hù)端調(diào)用:
..............
String url = "http://localhost:8888/hession/hessionService";
HessionPorxyFactory factory = new HessionPorxyFactory();
//獲得Hession服務(wù)的遠(yuǎn)程調(diào)用
Hello h = (Hello) factory.create(Hello.class,url);
//調(diào)用遠(yuǎn)程服務(wù)
System.out.println(d.hello("xixi"));
..............
2、servlet采用Spring的DispatcherServlet來(lái)提供服務(wù),客戶(hù)端采用Spring的HessionProxyFactoryBean連接Hession服務(wù)。
首先需要在web.xml中配置DispatchetServlet。
其次建立[servlet-name]-servlet.xml文件,該方件用于配置對(duì)外提供的服務(wù),這里要用到Spring框架提供的HessianServiceExporter來(lái)定義輸出的服務(wù)。它的功能就是把普通Bean定義可對(duì)外提供服務(wù)的Bean。這是由于DispatchetServlet查找提供服務(wù)的Bean時(shí),是通過(guò)與配置的Servlet名字相對(duì)應(yīng)的[servlet-name]-servlet.xml,
客戶(hù)端采用HessionProxyFactoryBean主要是利用Spring的IOC,否則的話(huà)完全可以用第一種方法。在客戶(hù)端的Spring配置文件中,用HessionProxyFactoryBean把遠(yuǎn)程提供服務(wù)的Bean(對(duì)應(yīng)上面servletname-servlet.xml中配置的輸出服務(wù)bean)配置到本地BeanFactory,使用時(shí)如同使用本地Bean方法一樣。
二、HttpInvoker:
使用 HttpInvoker,不需要額外的類(lèi)庫(kù),與Hession的輕量級(jí)傳輸協(xié)議不同,Spring HttpInvoker使用Java序列化來(lái)序列化參數(shù)和返回值,然后基于Http協(xié)議傳輸經(jīng)序列化后的對(duì)象,當(dāng)參數(shù)或返回值是復(fù)雜類(lèi)型,并且不能通過(guò)Hession的序列化機(jī)制序列化時(shí),HttpInvoker就很有優(yōu)的勢(shì)。
它的用法與Hession非常相似。Spring使用HttpInvokerServiceExporter把普通bean實(shí)例輸出成遠(yuǎn)程對(duì)象??蛻?hù)端連接Spring提供了HttpInvokerProxyFactoryBean工廠(chǎng)連接服務(wù),類(lèi)似于HessionProxyFactoryBean,配置時(shí)只需指定服務(wù)URL和服務(wù)實(shí)現(xiàn)接口,通過(guò)代理,Spring可將調(diào)用轉(zhuǎn)換成POST請(qǐng)求發(fā)送到指定服務(wù)。
三、RMI:
客戶(hù)端和服務(wù)器端必須是純Java實(shí)現(xiàn)。RMI服務(wù)是典型的面向接口編程,只有在遠(yuǎn)程接口里定義的方法才會(huì)作為遠(yuǎn)程服務(wù),遠(yuǎn)程方法的返回值和參數(shù)都必須實(shí)現(xiàn)Serializable接口,因?yàn)檫h(yuǎn)程在網(wǎng)絡(luò)上傳輸只能傳輸字節(jié)流,因此,要求參數(shù)、返回值都可以轉(zhuǎn)換成字節(jié)流-即實(shí)現(xiàn)序列化。
一、傳統(tǒng)使用方法:
?。?、遠(yuǎn)程服務(wù)提供類(lèi)必須實(shí)現(xiàn)遠(yuǎn)程接口(java.rmi.Remote)并繼承java.rmi.server.UnicastRemoteObject對(duì)象。遠(yuǎn)程服務(wù)類(lèi)必須有構(gòu)造器,而且構(gòu)造器必須拋出RemoteException異常。
2、注冊(cè)服務(wù):
Server imp = new ServerImpl(); //創(chuàng)建遠(yuǎn)程服務(wù)類(lèi)實(shí)例
LocateRegistry.createRegistry(1099); //注冊(cè)遠(yuǎn)程服務(wù)的端口
Naming.rebind("rmi://:1099/fdf",imp); //將遠(yuǎn)程服務(wù)實(shí)例綁定為遠(yuǎn)程服務(wù)
?。场?duì)于使用RMI,將源文件存盤(pán)編譯還不夠,還必須使用rmic命令編譯服務(wù)類(lèi),編譯服務(wù)類(lèi)是為了行成stub和skeleton。
4、客戶(hù)端要面向接口編程,客戶(hù)端部分需要服務(wù)實(shí)現(xiàn)接口的.class文件和生成的stub文件??蛻?hù)端調(diào)用示例代碼如下:
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的具體實(shí)現(xiàn),依然是依賴(lài)于底層的Socket編程。RMI依賴(lài)于TCP/IP傳輸協(xié)議,服務(wù)器端skeleton建立ServerSocket監(jiān)聽(tīng)請(qǐng)求,而客戶(hù)端建立Socket請(qǐng)求邊接。RMI實(shí)現(xiàn)網(wǎng)絡(luò)傳輸?shù)亩嗑€(xiàn)程、IO等底層細(xì)節(jié)。這些細(xì)節(jié)的實(shí)現(xiàn)就隱藏在rmic命令的執(zhí)行中,使用rmic命令編譯時(shí)生成如下兩個(gè)class文件:
stub:該文件用于與客戶(hù)端交流,建立Socket請(qǐng)求連接。
skeleton:該文件用于與服務(wù)端門(mén)交流,建立ServerSocket監(jiān)聽(tīng)請(qǐng)求。
二、Spring封裝
1、服務(wù)器端的接口不用實(shí)現(xiàn)Remote接口,實(shí)現(xiàn)類(lèi)也不用繼承UnicastRemoteObject類(lèi),都是普通的Java接口和類(lèi)。
2、如果要暴露遠(yuǎn)程方法,Spring提供了RmiServiceExporter類(lèi),該類(lèi)可以將一個(gè)普通Bean實(shí)例綁定成遠(yuǎn)程服務(wù)。將普通bean實(shí)例綁定為遠(yuǎn)程服務(wù)的完整配置如下:
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- 指定暴露出來(lái)的遠(yuǎn)程服務(wù)名,可任意取名字,但客戶(hù)端要用到該名字-->
<property name="serviceName">
<value>RealtimeTransferService</value>
</property>
<!--配置暴露目標(biāo)-->
<property name="service" ref="realtimeTransfer" />
<!--配置bean實(shí)現(xiàn)的接口,該接口被當(dāng)作遠(yuǎn)程接口對(duì)待-->
<property name="serviceInterface"
value="com.topnet.tais.prefixmachine.business.realtimepay.RealtimeTransfer" />
<!--指定RMI遠(yuǎn)程服務(wù)的端口號(hào)-->
<property name="registryPort">
<value>1099</value>
</property>
3、客戶(hù)端訪(fǎng)問(wèn)方式。
<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主要有兩個(gè)版本:1.0.2和1.1,二者的區(qū)別:JMS1.0.2對(duì)兩種消息模型提供了不同的類(lèi)體系。JMS1.1則使用統(tǒng)一模型的概念,從而減少兩種模型之間的差別,避免客戶(hù)端代碼的差別。
消息的兩種模型:
點(diǎn)對(duì)點(diǎn)消息處理:這種消息處理模型為應(yīng)用中的各個(gè)邏輯處理單元提供可靠的通信支持,JMS系統(tǒng)保證消息傳遞給消息接收者,不會(huì)同被多個(gè)接收者接收,如果消息接收者暫不在連接范圍內(nèi),JMS保證消息不會(huì)丟失,直到接收者進(jìn)入連接,消息將自動(dòng)送達(dá)。因此,JMS將消息保存到永久性介質(zhì),如數(shù)據(jù)庫(kù)或文件上。
發(fā)布/訂閱消息處理:
使用這種模型,可以將消息發(fā)送到一個(gè)主題,每個(gè)子主題可以有多個(gè)訂閱者。JMS系統(tǒng)負(fù)責(zé)將消息的副本傳給主題的每個(gè)訂閱者。
JMS開(kāi)發(fā):
發(fā)送步驟:1、連接工廠(chǎng)創(chuàng)建JMS連接;2、JMS連接創(chuàng)建JMS會(huì)話(huà);3、JMS會(huì)話(huà)創(chuàng)建消息生產(chǎn)者;4、JMS會(huì)話(huà)創(chuàng)建空JMS消息;5、JMS消息調(diào)用自身的主法填充內(nèi)容;6、JMS消息生產(chǎn)者發(fā)送消息。
接收步驟:1、連接工廠(chǎng)創(chuàng)建JMS連接;2、JMS連接創(chuàng)建JMS會(huì)話(huà);3、JMS會(huì)話(huà)創(chuàng)建JMS消費(fèi)者;4、JMS消費(fèi)者接收消息,同步和異步接收消息方工略有差異。
Spring對(duì)JMS的支持:
?。?、提供了JmsTemplate
2、管理連接工廠(chǎng),Spring提供了一個(gè)ConnectionFactory的實(shí)現(xiàn)SingleConnectionFactory,該連接工廠(chǎng)對(duì)所有的createConnection調(diào)用返回同一個(gè)連接,并忽略close的調(diào)用。這在測(cè)試和獨(dú)立的環(huán)境中相當(dāng)有用,只有同一個(gè)連接被用于多個(gè)JmsTemplate,這樣才可以跨越多個(gè)事務(wù)。創(chuàng)建SingleConnectionFactory必須提供一個(gè)標(biāo)準(zhǔn)ConnectionFactory的引用,作為目標(biāo)連接工廠(chǎng),目票連接工廠(chǎng)需要應(yīng)用服務(wù)器提供。
3、管理消息目的,消息生產(chǎn)者的消息目的也是消息消費(fèi)提取消息的消息源,當(dāng)配置Spring應(yīng)用上下文時(shí),可以使用工廠(chǎng)類(lèi) JndiObjectFactory配置消息目的。布爾屬性PubSubDomain用來(lái)配置JmsTemplate是否使用Pub/Sub模型,默認(rèn)值是false,消息隊(duì)列模型。該屬性對(duì)JMS1.1沒(méi)有影響。DefaultDestionation屬性用于配置JmsTemplate的默認(rèn)目的,如果配置了默認(rèn)目的,JmsTemplate發(fā)送和接收操作可以無(wú)須指定消息目的。
4、JMS與事務(wù),Spring提供一個(gè)JmsTransactionManager來(lái)管理事務(wù),它對(duì)于SingleConnectionFactory有效。JmsTransactionManger將Connection/Session對(duì)綁定到線(xiàn)程,然而在一個(gè)J2EE環(huán)境中,ConnectionFactory將緩存連接和會(huì)話(huà),所以被綁定到線(xiàn)程的實(shí)例依賴(lài)于緩存行為。JmsTempate也能和JtaTransactionManager一起使用,以完成分布式事務(wù)。
Spring中的配置:
?。薄NDI上下文是取得JMS資源的起始位置,因此首先要配置JNDI模板,如下所示:
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<description>jndi環(huán)境配置</description>
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
</props>
</property>
</bean>
?。病⑴渲藐?duì)列連接工廠(chǎng)
<bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<description>jms連接工廠(chǎng)</description>
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>Toptais3Ext-JMSConnectionFactory</value>
</property>
</bean>
?。?、配置消息目的
<bean id="destinationBatchStartQueue" class="org.springframework.jndi.JndiObjectFactoryBean">
<description>頁(yè)面發(fā)起批量代扣請(qǐng)求的長(zhǎng)事件Queue </description>
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>Toptais3Ext-BatchStartQueue</value>
</property>
</bean>
?。?、配置JmsTemplate
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate102">
<description>發(fā)送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、配置發(fā)送和接收bean
五、EJB:
六、WebService: