Spring in Action
學習筆記
—
第六章
遠程調用 遠程調用是用戶端應用和服務端之間的會話。在用戶端上所需要的一些功能並不包括在該應用的職能範圍內。所以應用向能提供這些功能的其他系統尋求協助。遠端應用通過遠程服務把這些功能公開出來。
一、
Spring
遠程調用概覽 Spring為各種遠端存取技術的整合提供了工具類。Spring遠程支援是由普通(Spring)POJO實現的,這使得開發具有遠端存取功能的服務變得相當容易。 Spring遠程調用支援6種不同的RPC模式:遠程方法調用(RMI)、Caucho的Hessian和Burlap、Spring自己的HTTP invoker、EJB和使用JAX-RPC 的Web Services。
| RPC 模式 |
在何種情況下有用 |
| 遠程方法調用( RMI ) |
不考慮網路限制(如防火牆)時,訪問 / 公開基於 Java 的服務 |
| Hessian 或 Burlap |
考慮網路限制時,通過 HTTP 訪問 / 公開基於 Java 的服務 |
| HTTP invoker |
考慮網路限制時,訪問 / 公開基於 Spring 的服務 |
| EJB |
訪問用 EJB 實現的遺留的 J2EE 系統 |
| JAX-RPC |
訪問 Web Services |
其中(來自Spring2.0參考手冊): l 遠程方法調用(RMI)。通過使用 RmiProxyFactoryBean和 RmiServiceExporter,Spring同時支援傳統的RMI(使用java.rmi.Remote介面和java.rmi.RemoteException)和通過RMI調用器實現的透明遠程調用(支援任何Java介面)。 l Spring的HTTP調用器。Spring提供了一種特殊的允許通過HTTP進行Java序列化的遠程調用策略,支援任意Java介面(就像RMI調用器)。相對應的支援類是 HttpInvokerProxyFactoryBean和 HttpInvokerServiceExporter。 l Hessian。通過 HessianProxyFactoryBean和 HessianServiceExporter,可以使用Caucho提供的基於HTTP的輕量級二進位協議來透明地暴露服務。 l Burlap。 Burlap是Caucho的另外一個子項目,可以作為Hessian基於XML的替代方案。Spring提供了諸如 BurlapProxyFactoryBean和 BurlapServiceExporter的支援類。 l JAX RPC。Spring通過JAX-RPC為遠程Web服務提供支援。 不管選擇哪種遠程模式,你會發現Spring對每一種模式的支援中貫穿著一個共同的風格。這就意味著你一旦理解了Spring如何配置並使用其中的一種模式,當你決定使用另一種不同的模式的時候,你將擁有非常低的學習曲線。 在所有的模式中,服務可以作為Spring管理的Bean配置到你的應用中。這是用一個代理工廠Bean實現的,這個Bean使你能把遠程服務當作本機物件一樣置入到其他Bean的屬性中。 用戶端發起對代理的調用,好像是代理提供了這些服務的功能一樣。代理代表用戶端和遠程服務交流。它處理串連的具體情況,並向遠程服務發起遠程調用。 在服務端,你能夠把任何Spring管理的Bean的功能公開成為一個遠程服務,可使用在表6.1中所列的任何模式(除了EJB和JAX-RPC)。 不論開發的是使用遠程服務的代碼,還是實現那些服務的代碼,或者二者兼而有之,在Spring中,使用遠程服務純粹是個配置問題。你不用寫任何Java代碼來支援遠程調用。你的服務Bean不必關心它們是否被捲入到RPC裡(雖然任何傳遞給遠程調用的Bean或從遠程調用返回的Bean可能需要實現java.io.Serializable)。
二、與
RMI
一起工作
1
.串連
RMI
服務 Spring的RmiProxyFactoryBean是一個工廠Bean,能建立一個指向RMI服務的代理。用RmiProxyFactoryBean來引用一個RMI PaymentService是非常簡單的,只要在Spring設定檔中聲明下面的<bean>: <bean id="paymentService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> <property name="serviceUrl"> <value>rmi://${paymenthost}/PayService</value> </property> <property name="serviceInterface"> <value>com.springinaction.payment.PaymentService</value> </property> </bean> RMI服務的URL是通過serviceUrl屬性設定的。這裡,服務被命名為PayService,並且是在一台名字用一個屬性預留位置配置的機器上(參考第2章的2.4.3節)。serviceInterface屬性指明了這個服務所實現的介面,用戶端通過這個介面調用在這個服務裡的方法。 把這個支付服務定義為一個Spring管理的Bean,你就能把它作為一個合作者置入到另外的Bean上,就像你對任何非遠端Bean做的那樣。舉例來說,假設StudentServiceImpl需要使用這個支付服務來批准一張信用卡的支付。你就使用以下代碼把RMI服務置入到StudentServiceImpl中: <bean id="studentService" class="com.springinaction.training.service.StudentServiceImpl"> … <property name="paymentService"> <ref bean="paymentService"/> </property> … </bean> StudentServiceImpl甚至不需要知道它處理的是一個RMI服務。它只是通過注入機制接收PaymentService對象,不必關心它是從哪裡來的。此外,代理會捕獲任何可能被這個服務拋出的RemoteException,並把它們作為已耗用時間異常重新拋出,這樣,你可以安全地忽略這些異常。這也讓遠程服務Bean和這個服務的另外實現之間的交換成為可能——或許是不同的遠程服務,或者有可能是單元測試時的一個類比實現。
2
.輸出
RMI
服務 Spring提供了比較簡單的發布RMI服務的方法:使用POJO。開始之前,你需要寫這個服務的介面:
public
interface PaymentService {
public String authorizeCreditCard(String cardNumber, String cardHolderName,
int expireMonth,
int expireYear,
float amount)
throws AuthorizationException;
public
void settlePayment(String authCode,
int merchantNumber,
float amount)
throws SettlementException; } 由於服務介面不是從java.rmi.Remote繼承的,它的方法都不拋出java.rmi.RemoteException,這點讓這個介面簡短了很多。但更重要的是,用戶端通過這個介面訪問支付服務時,將不再需要捕捉那些它們可能沒法處理的異常了。下一步,定義服務的實作類別:
public
class PaymentServiceImpl
implements PaymentService {
public PaymentServiceImpl() {}
public String authorizeCreditCard(String creditCardNumber, String cardHolderName,
int expirationMonth,
int expirationYear,
float amount)
throws AuthorizationException { // String authCode = ...; // implement authorization
return authCode; }
public
void settlePayment(String authCode,
int accountNumber,
float amount)
throws SettlementException { // implement settlement } } 你要做的下一件事就是在Spring的設定檔裡把PaymentServiceImpl配置為一個<bean>: <bean id="paymentService" class="org.springframework.payment.PaymentServiceImpl"> … </bean> PaymentServiceImpl沒有設定RMI所固有的特性。它僅僅是一個適合在Spring設定檔中聲明的簡單的POJO。事實上,完全有可能在非遠程方式中,通過直接把它置入到用戶端裡,來使用這個實現。
三、使用
Hessian
和
Burlap
的遠程調用 Hessian和Burlap是Caucho Technology(http://www.caucho.com)提供的兩種解決方案,是基於HTTP的輕量級遠程服務。它們都致力於通過把它們的API和通訊協定變得儘可能的簡單,來簡化Web服務。 事實上,Hessian和Burlap是同一個問題的兩個方面,但每個都服務於略微不同的目的。Hessian,像RMI那樣,使用二進位訊息來建立用戶端和服務端之間的交流。但與其他二進位遠程技術(如RMI)不同的是,它的二進位訊息可以移植到其他非Java的語言中。 Burlap是一種基於XML的遠程技術,這使得它自然而然地可以移植到任何可以解析XML的語言中。正由於它的XML,比起Hessian的二進位格式來,它的可讀性更強。但和其他基於XML的遠程技術(例如SOAP或XML-RPC)不同,Burlap的訊息結構是儘可能的簡單,不需要額外的外部定義語言(如WSDL或IDL等) [1] 。 如何在Hessian和Burlap之間做選擇。很大程度上說,它們是一樣的。惟一的不同就是Hessian的訊息是二進位的,而Burlap的訊息是XML。由於Hessian的訊息是二進位的,所以它在頻寬上更佔優勢。但如果可讀性對你來說很重要的話(如出於調試的目的)或者你的應用將和沒有Hessian實現(任何除了Java或Python)的語言交流,那麼Burlap的XML訊息會是更好的選擇。
1
.訪問
Hessian/Burlap
服務 所有RMI的細節都包含在Spring設定檔的Bean的配置裡。這樣做的好處就是,由於用戶端忽略了服務的實現,從一個RMI用戶端轉到Hessian用戶端是極其簡單的,不需要改變任何用戶端代碼。 壞處就是,如果你真地喜歡寫代碼的話,那麼這一節就可能讓你有點兒失望了。因為寫基於RMI服務的用戶端代碼和基於Hessian服務的用戶端代碼惟一的不同就是你將使用Spring的HessianProxyFactoryBean來代替RmiProxyFactoryBean。用戶端代碼中基於Hessian的支付服務可以用以下代碼聲明: <bean id="paymentService" class="org.springframework. ➥remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl"> <value>http://${serverName}/${contextPath}/pay.service</value> </property> <property na