Spring遠程訪問
前言
對于不適用EJB的Java EE應用來說,構建分布式應用的常見方式就是RMI(remote method invocation,Java遠程方式調用),他使的運行在一個JVM上的Java對象能被另一個JVM訪問到。
RMI相當復雜,而EJB引入的一個地方就在與他對開發者隱藏了底層RMI的細節。
Spring提供了成為Spring Remoting的全面的特性集來簡化分布式應用的創建。
Spring的遠程訪問支持:
RMI:RMI是J2SE的一部分,用來創建分布式應用。Spring對RMI的支持可以減少公開和訪問RMI服務的代碼量,同時還幫我們處理了RMI中的大多數繁文縟節,如RemoteException的處理等。Spring還集成了RMI和JNDI,這對于CORBA服務的公開和訪問來說用處是非常大的。
JAX-RPC:即“Java API for XML-based Remote Procedure Calls”,他為訪問和公開RPC風格的SOAP Web服務提供了標準的Java API。Spring提供了支持類以簡化JAX-RPC客戶端應用的創建和基于Servlet的服務端點的創建。就像很多Java XML API一樣,JAX-RPC也擁有很多實現,其中最流行的是Apache Axis,他是完全兼容于JAX-RPC的SOAP棧。本章的JAX-RPC示例將使用Axis實現。
JAX-WS:JAX-WS 2.0是JAX-RPC的 1.1的繼任者。他提供了用于公開和訪問SOAP 1.2Web服務及創建客戶端應用的API。本章 的JAX0WS示例將使用XFire實現。
HTTP Invoker:HTTP Invoker架構是Spring的本地遠程架構,他使用標準的Java序列化和HTTP為遠程組件的構建提供了簡單的解決方案。HTTP Invoker使用服務器端的Servlet容器來存放遠程服務。這么做的好處是可以使用HTTP認證方法增強遠程服務的安全性。
Hessian:Hessian是有Caucho創建的一個二進制協議,用以簡化Web服務的創建。他特定于任何特定的傳輸方式,但通常使用HTTP。 Spring為其提供了支持類以簡化Hessian服務的創建,他使用HTTP進行傳輸并提供了代理支持,這樣就可以透明的訪問Hessian服務了。
Burlap:Burlap也是由Caucho創建的,他是一個基于XML的協議,對Hessian協議是個補充。除了協議的細節之外,Burlap的使用方式與Hessian一樣。Spring對Burlap的支持與對Hessian的支持差不多,事實上,在需要交換這兩個協議時,Burlap類可以輕松替換掉Hessian類。
Spring Remoting架構
Spring Remoting架構的核心是一個服務輸出器(service exporter)和一個代理生成器(proxy generator)的概念。
一旦公開了一個遠程服務,接下來就需要創建一個客戶端來訪問這個服務。通常這是構建分布應用最復雜的之一,因為你需要密切關注所選的遠程架構。
借助于Spring,你可以通過一個代理生成器創建遠程資源的代理,這樣就可以使用簡單的Java接口訪問遠程服務了。
這種方式不僅降低了客戶端代碼的復雜度(由Spring處理遠程架構相關事宜),而且還將應用于所選的遠程架構進行了解耦(Spring隱藏了所有的是實現細節)。
Spring所支持的五種遠程架構中的四種都可以使用這兩個組件。而JAX-RPC/JAX-WS卻沒有服務輸出器,因為公開服務的方法取決于你使用的 JAX-RPC/JAX-WS實現。然而Spring提供了ServletEndpointSupport類來簡化JAX-RPC服務點(service endpoint)的創建,將其公開為一個servlet。
遠程訪問調用
自從1.1版本開始,RMI就已經成為Java的一部分,而且在很多遠程方案中他都處于中心位置。Java中的CORBA支持就是通過RMI實現的,EJB也將RMI作為bean通訊的底層機制。JAX-RPC構建在RMI的概念之上以將Java對象公開成Web服務。Java中的CORBA支持也可以通過接口定義語言(interface definition language,IDL)來實現。
IDL是一種聲明性語言,可以創建彼此交互的對象---這些對象可能是不同的編程語言所創建的。
然而當前RMI在Java中使用的更廣泛,這是由于其更好的安全性和垃圾收集能力。
1、開放任意的服務
構建RMI服務,通常你需要為服務定義一個接口,該接口繼承自java.rmi.Remote接口。然后你的RMI服務需要實現該接口,最好還繼承java.rmi.server.UnicastRemoteObject類。
Spring Remoting,通過RmiServiceExporter類簡化上面的操作。
demo:
我們聲明了兩個bean:helloWorldService bean(要被公開的bean)和serviceExporter bean(將helloWorldService bean公開的RMI服務)。
package cn.partner4java.remoting;public interface HelloWorld { public String getMessage(); }
package cn.partner4java.remoting;
public class SimpleHelloWorld implements HelloWorld {
public String getMessage() { return "Hello World"; }
}
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop ; <bean id="helloWorldService" class="cn.partner4java.remoting.SimpleHelloWorld" /> <bean id="serviceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter"> <!-- 服務名 --> <property name="serviceName" value="HelloWorld" />
<property name="service" ref="helloWorldService" /> <property name="serviceInterface" value="cn.partner4java.remoting.HelloWorld" /> <!-- RMI registry的端口號 --> <property name="registryPort" value="9000" /> <!-- 用戶通訊的服務端口。默認為0,表示使用一個匿名端口號 --> <property name="servicePort" value="9001" /> </bean>
</beans></pre>2、通過代理訪問RMI服務
借助于代理生成器,Spring會生成實現遠程接口的那個遠程服務的代理,這樣你就可以通過服務接口與遠程服務進行交互了,就好像他是本地組件一樣。Spring隱藏了所有RMI細節。
就像所有的代理生成器一樣,RMI代理生成器也實現了FactoryBean接口,這樣我們就可以通過聲明的方式創建并配置代理,然后將其做為依賴注入到組件中了。
demo:package cn.partner4java.remoting.rmi;import cn.partner4java.remoting.HelloWorld;
public class HelloWorldClient { private HelloWorld helloWorldService;
public void setHelloWorldService(HelloWorld helloWorldService) { this.helloWorldService = helloWorldService; } public void run(){ System.out.println(helloWorldService.getMessage()); }
}
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context ;
<bean id="helloWorldService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <!-- 指向了RMI服務上的代理 --> <property name="serviceUrl" value="rmi://localhost:9000/HelloWorld" /> <!-- 告訴代理生成器生成的接口應該實現哪個接口 --> <property name="serviceInterface" value="cn.partner4java.remoting.HelloWorld"/> </bean> <bean id="helloWorldClient" class="cn.partner4java.remoting.rmi.HelloWorldClient"> <property name="helloWorldService" ref="helloWorldService" /> </bean>
</beans></pre>3、開放CORBA服務
RMI一個很棒的特性是他使你可以通過Internet Inter-Orb Protocol(IIOP)公開服務,這樣使用其他語言編寫的CORBA組件就可以訪問這些服務了。對于不同語言編寫的組件之間的交互來說,CORBA 是一個流行的解決方案,同時很多程序語言也對其進行了擴展支持。
通過RMI并使用IIOP而不是默認的Java遠程方法協議(Java remote method protocol,JRMP)公開Java服務只需做以下兩件事:生成正確的代碼樁并通過CORBA Object Request Broker(ORB)公開服務。當使用JRMP公開服務時,Spring會令你的組件與RMI基礎設施解耦,同時也無需創建樁了。當公開CORBA服務時,盡管Spring負責用ORB來注冊服務,但他的用處卻不那么大。
事實上,Spring并沒有使用類來公開CORBA服務以達到簡化的目的,相反他是通過JNDI來公開并尋找遠程服務。當處理CORBA組建時,使用JNDI進行服務查找是最有效的,因為應用可以通過JNDI與ORB交互。其他的服務的具體實現可查看《Spring高級程序設計》