隨筆-28  評(píng)論-15  文章-81  trackbacks-0

          遠(yuǎn)程調(diào)用是客戶端應(yīng)用和服務(wù)端之間的會(huì)話。在客戶端上所需要的一些功能并不包括在該應(yīng)用的職能范圍內(nèi)。所以應(yīng)用向能提供這些功能的其他系統(tǒng)尋求幫助。遠(yuǎn)程的應(yīng)用通過遠(yuǎn)程服務(wù)把這些功能公開出來。

           

          一、Spring遠(yuǎn)程調(diào)用概覽

          Spring為各種遠(yuǎn)程訪問技術(shù)的集成提供了工具類。Spring遠(yuǎn)程支持是由普通(SpringPOJO實(shí)現(xiàn)的,這使得開發(fā)具有遠(yuǎn)程訪問功能的服務(wù)變得相當(dāng)容易。

          Spring遠(yuǎn)程調(diào)用支持6種不同的RPC模式:遠(yuǎn)程方法調(diào)用(RMI)、CauchoHessianBurlapSpring自己的HTTP invokerEJB和使用JAX-RPC Web Services

          RPC模式

          在何種情況下有用

          遠(yuǎn)程方法調(diào)用(RMI

          不考慮網(wǎng)絡(luò)限制(如防火墻)時(shí),訪問/公開基于Java的服務(wù)

          Hessian Burlap

          考慮網(wǎng)絡(luò)限制時(shí),通過HTTP訪問/公開基于Java的服務(wù)

          HTTP invoker

          考慮網(wǎng)絡(luò)限制時(shí),訪問/公開基于Spring的服務(wù)

          EJB

          訪問用EJB實(shí)現(xiàn)的遺留的J2EE系統(tǒng)

          JAX-RPC

          訪問Web Services

          其中(來自Spring2.0參考手冊(cè)):

          l         遠(yuǎn)程方法調(diào)用(RMI。通過使用 RmiProxyFactoryBean RmiServiceExporterSpring同時(shí)支持傳統(tǒng)的RMI(使用java.rmi.Remote接口和java.rmi.RemoteException)和通過RMI調(diào)用器實(shí)現(xiàn)的透明遠(yuǎn)程調(diào)用(支持任何Java接口)。

          l         SpringHTTP調(diào)用器Spring提供了一種特殊的允許通過HTTP進(jìn)行Java串行化的遠(yuǎn)程調(diào)用策略,支持任意Java接口(就像RMI調(diào)用器)。相對(duì)應(yīng)的支持類是 HttpInvokerProxyFactoryBean HttpInvokerServiceExporter

          l         Hessian。通過 HessianProxyFactoryBean HessianServiceExporter,可以使用Caucho提供的基于HTTP的輕量級(jí)二進(jìn)制協(xié)議來透明地暴露服務(wù)。

          l         Burlap BurlapCaucho的另外一個(gè)子項(xiàng)目,可以作為Hessian基于XML的替代方案。Spring提供了諸如 BurlapProxyFactoryBean BurlapServiceExporter的支持類。

          l         JAX RPCSpring通過JAX-RPC為遠(yuǎn)程Web服務(wù)提供支持。

          不管選擇哪種遠(yuǎn)程模式,你會(huì)發(fā)現(xiàn)Spring對(duì)每一種模式的支持中貫穿著一個(gè)共同的風(fēng)格。這就意味著你一旦理解了Spring如何配置并使用其中的一種模式,當(dāng)你決定使用另一種不同的模式的時(shí)候,你將擁有非常低的學(xué)習(xí)曲線。

          在所有的模式中,服務(wù)可以作為Spring管理的Bean配置到你的應(yīng)用中。這是用一個(gè)代理工廠Bean實(shí)現(xiàn)的,這個(gè)Bean使你能把遠(yuǎn)程服務(wù)當(dāng)作本地對(duì)象一樣置入到其他Bean的屬性中。

          客戶端發(fā)起對(duì)代理的調(diào)用,好像是代理提供了這些服務(wù)的功能一樣。代理代表客戶端和遠(yuǎn)程服務(wù)交流。它處理連接的具體情況,并向遠(yuǎn)程服務(wù)發(fā)起遠(yuǎn)程調(diào)用。

          在服務(wù)端,你能夠把任何Spring管理的Bean的功能公開成為一個(gè)遠(yuǎn)程服務(wù),可使用在表6.1中所列的任何模式(除了EJBJAX-RPC)。

          不論開發(fā)的是使用遠(yuǎn)程服務(wù)的代碼,還是實(shí)現(xiàn)那些服務(wù)的代碼,或者二者兼而有之,在Spring中,使用遠(yuǎn)程服務(wù)純粹是個(gè)配置問題。你不用寫任何Java代碼來支持遠(yuǎn)程調(diào)用。你的服務(wù)Bean不必關(guān)心它們是否被卷入到RPC里(雖然任何傳遞給遠(yuǎn)程調(diào)用的Bean或從遠(yuǎn)程調(diào)用返回的Bean可能需要實(shí)現(xiàn)java.io.Serializable)。

           

          二、與RMI一起工作

          1.連接RMI服務(wù)

          SpringRmiProxyFactoryBean是一個(gè)工廠Bean,能創(chuàng)建一個(gè)指向RMI服務(wù)的代理。用RmiProxyFactoryBean來引用一個(gè)RMI PaymentService是非常簡(jiǎn)單的,只要在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服務(wù)的URL是通過serviceUrl屬性設(shè)置的。這里,服務(wù)被命名為PayService并且是在一臺(tái)名字用一個(gè)屬性占位符配置的機(jī)器上(參考第2章的2.4.3節(jié))。serviceInterface屬性指明了這個(gè)服務(wù)所實(shí)現(xiàn)的接口,客戶端通過這個(gè)接口調(diào)用在這個(gè)服務(wù)里的方法。

          把這個(gè)支付服務(wù)定義為一個(gè)Spring管理的Bean,你就能把它作為一個(gè)合作者置入到另外的Bean上,就像你對(duì)任何非遠(yuǎn)程的Bean做的那樣。舉例來說,假設(shè)StudentServiceImpl需要使用這個(gè)支付服務(wù)來批準(zhǔn)一張信用卡的支付。你就使用以下代碼把RMI服務(wù)置入到StudentServiceImpl中:

          <bean id="studentService"

           

                 class="com.springinaction.training.service.StudentServiceImpl">

           

           …

           

              <property name="paymentService">

           

                <ref bean="paymentService"/>

           

              </property>

           

           …

           

           </bean>

          StudentServiceImpl甚至不需要知道它處理的是一個(gè)RMI服務(wù)。它只是通過注入機(jī)制接收PaymentService對(duì)象,不必關(guān)心它是從哪里來的。此外,代理會(huì)捕獲任何可能被這個(gè)服務(wù)拋出的RemoteException,并把它們作為運(yùn)行時(shí)間異常重新拋出,這樣,你可以安全地忽略這些異常。這也讓遠(yuǎn)程服務(wù)Bean和這個(gè)服務(wù)的另外實(shí)現(xiàn)之間的交換成為可能——或許是不同的遠(yuǎn)程服務(wù),或者有可能是單元測(cè)試時(shí)的一個(gè)模擬實(shí)現(xiàn)。

          2.輸出RMI服務(wù)

          Spring提供了比較簡(jiǎn)單的發(fā)布RMI服務(wù)的方法:使用POJO。開始之前,你需要寫這個(gè)服務(wù)的接口:

          publicinterface PaymentService

                 {

                     public String authorizeCreditCard(String cardNumber, String cardHolderName, int expireMonth, int expireYear, float amount) throws AuthorizationException;

           

                     publicvoid settlePayment(String authCode, int merchantNumber, float amount) throws SettlementException;

                 }

          由于服務(wù)接口不是從java.rmi.Remote繼承的,它的方法都不拋出java.rmi.RemoteException這點(diǎn)讓這個(gè)接口簡(jiǎn)短了很多。但更重要的是,客戶端通過這個(gè)接口訪問支付服務(wù)時(shí),將不再需要捕捉那些它們可能沒法處理的異常了。下一步,定義服務(wù)的實(shí)現(xiàn)類:

          publicclass 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;

                     }

           

                     publicvoid settlePayment(String authCode, int accountNumber, float amount) throws SettlementException

                     {

                       // implement settlement

                     }

                  }

          你要做的下一件事就是在Spring的配置文件里把PaymentServiceImpl配置為一個(gè)<bean>

          <bean id="paymentService" class="org.springframework.payment.PaymentServiceImpl">

           …

          </bean>

          PaymentServiceImpl沒有設(shè)置RMI所固有的特性。它僅僅是一個(gè)適合在Spring配置文件中聲明的簡(jiǎn)單的POJO。事實(shí)上,完全有可能在非遠(yuǎn)程方式中,通過直接把它置入到客戶端里,來使用這個(gè)實(shí)現(xiàn)。

           

          三、使用HessianBurlap的遠(yuǎn)程調(diào)用

          HessianBurlapCaucho Technologyhttp://www.caucho.com)提供的兩種解決方法,是基于HTTP的輕量級(jí)遠(yuǎn)程服務(wù)。它們都致力于通過把它們的API和通信協(xié)議變得盡可能的簡(jiǎn)單,來簡(jiǎn)化Web服務(wù)。

          事實(shí)上,HessianBurlap是同一個(gè)問題的兩個(gè)方面,但每個(gè)都服務(wù)于略微不同的目的。Hessian,像RMI那樣,使用二進(jìn)制消息來建立客戶端和服務(wù)端之間的交流。但與其他二進(jìn)制遠(yuǎn)程技術(shù)(如RMI)不同的是,它的二進(jìn)制消息可以移植到其他非Java的語言中。

          Burlap是一種基于XML的遠(yuǎn)程技術(shù),這使得它自然而然地可以移植到任何可以解析XML的語言中。正由于它的XML,比起Hessian的二進(jìn)制格式來,它的可讀性更強(qiáng)。但和其他基于XML的遠(yuǎn)程技術(shù)(例如SOAPXML-RPC)不同,Burlap的消息結(jié)構(gòu)是盡可能的簡(jiǎn)單,不需要額外的外部定義語言(如WSDLIDL等)

          如何在HessianBurlap之間做選擇。很大程度上說,它們是一樣的。惟一的不同就是Hessian的消息是二進(jìn)制的,而Burlap的消息是XML。由于Hessian的消息是二進(jìn)制的,所以它在帶寬上更占優(yōu)勢(shì)。但如果可讀性對(duì)你來說很重要的話(如出于調(diào)試的目的)或者你的應(yīng)用將和沒有Hessian實(shí)現(xiàn)(任何除了JavaPython)的語言交流,那么BurlapXML消息會(huì)是更好的選擇。

          1.訪問Hessian/Burlap服務(wù)

          所有RMI的細(xì)節(jié)都包含在Spring配置文件的Bean的配置里。這樣做的好處就是,由于客戶端忽略了服務(wù)的實(shí)現(xiàn),從一個(gè)RMI客戶端轉(zhuǎn)到Hessian客戶端是極其簡(jiǎn)單的,不需要改變?nèi)魏慰蛻舳舜a。

          壞處就是,如果你真地喜歡寫代碼的話,那么這一節(jié)就可能讓你有點(diǎn)兒失望了。因?yàn)閷懟?/span>RMI服務(wù)的客戶端代碼和基于Hessian服務(wù)的客戶端代碼惟一的不同就是你將使用SpringHessianProxyFactoryBean來代替RmiProxyFactoryBean。客戶端代碼中基于Hessian的支付服務(wù)可以用以下代碼聲明:

          <bean id="paymentService" class="org.springframework.

           

                    ?remoting.caucho.HessianProxyFactoryBean">

           

              <property name="serviceUrl">

           

                  <value>http://${serverName}/${contextPath}/pay.service</value>

           

              </property>

           

              <property name="serviceInterface">

           

                  <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

           </bean>

          就像基于RMI的服務(wù)那樣,serviceInterface屬性指定這個(gè)服務(wù)實(shí)現(xiàn)的接口。并且,如同RmiProxyFactoryBeanserviceUrl表明這個(gè)服務(wù)的URL。既然Hessian是基于HTTP的,當(dāng)然應(yīng)該在這里設(shè)置一個(gè)HTTP URL了(你將在下一節(jié)中看到這個(gè)URL是如何得來的)。

          事實(shí)證明,寫一個(gè)Burlap服務(wù)是同樣無趣的。二者惟一的不同就是,你使用BurlapProxyFactoryBean代替HessianProxyFactoryBean

          <bean id="paymentService" class="org.springframework.

           

                    ?remoting.caucho.BurlapProxyFactoryBean">

           

              <property name="serviceUrl">

           

                <value>http://${serverName}/${contextPath}/pay.service</value>

           

              </property>

           

              <property name="serviceInterface">

           

                <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

           </bean>

          盡管我們覺得在RMIHessianBurlap服務(wù)之間稍微不同的配置是很沒有樂趣的,但這個(gè)單調(diào)恰恰是有好處的。它意味著你不費(fèi)吹灰之力就可以在各種Spring支持的遠(yuǎn)程技術(shù)之間轉(zhuǎn)換,無須去學(xué)習(xí)一個(gè)全新的模型。你一旦配置了一個(gè)對(duì)RMI服務(wù)的引用,把它重新配置為HessianBurlap服務(wù)也是很輕松的工作。

          2.用HessianBurlap公開Bean的功能

          輸出一個(gè)Hessian服務(wù):

          Spring里輸出一個(gè)Hessian服務(wù)和在Spring里實(shí)現(xiàn)一個(gè)RMI服務(wù)驚人地相似。為把支付服務(wù)公開為RMI服務(wù),你得在Spring配置文件中配置一個(gè)RmiServiceExporter Bean。非常類似的,把支付服務(wù)公開為Hessian服務(wù),你也需要配置一個(gè)exporter Bean。只不過這一次用的是HessianServiceExporter

          <bean name="hessianPaymentService" class="org.springframework.

           

                   ?remoting.caucho.HessianServiceExporter">

           

              <property name="service">

           

                <ref bean="paymentService"/>

           

              </property>

           

              <property name="serviceInterface">

           

                <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

           </bean>

          HessianServiceExporterHessian服務(wù)中實(shí)現(xiàn)的功能和RmiServiceExporterRMI服務(wù)中的功能是完全一樣的。那就是說,它把一個(gè)Bean的公共方法公開為一個(gè)Hessian服務(wù)的方法。

          正如RmiServiceExporterservice屬性中被置入了實(shí)現(xiàn)這個(gè)服務(wù)的Bean的引用。這里,這個(gè)service屬性綁定的是paymentService Bean的引用。serviceInterface屬性用來表示PaymentService是這個(gè)服務(wù)所實(shí)現(xiàn)的接口。

          然而,和RmiServiceExporter不同,你不需要設(shè)置serviceName屬性。在RMI中,serviceName屬性用來在RMI注冊(cè)表中注冊(cè)一個(gè)服務(wù)。Hessian沒有注冊(cè)表,因此就沒有必要命名一個(gè)Hessian服務(wù)。

          配置Hessian控制器:

          RmiServiceExporterHessianServiceExporter另外一個(gè)主要的區(qū)別就是,由于Hessian是基于HTTP所以HessianServiceExporter被實(shí)現(xiàn)成Spring MVCController。這就是說,為了使用輸出的Hessian服務(wù),你需要完成兩個(gè)額外的配置步驟:

          1.在你的Spring配置文件中配置一個(gè)URL處理器,來分發(fā)Hessian服務(wù)的URL給適當(dāng)?shù)?/span>Hessian服務(wù)Bean

          2web.xml中配置一個(gè)SpringDispatcherServlet,并把你的應(yīng)用部署為web應(yīng)用。

          輸出一個(gè)Burlap服務(wù):

          Spring管理的Bean作為Burlap服務(wù)輸出。用SpringBurlapServiceExporter來代替HessianServiceExporter就能完成這項(xiàng)任務(wù):

          <bean name="burlapPaymentService" class="org.springframework.

           

                      ?remoting.caucho.BurlapServiceExporter">

           

              <property name="service">

           

                <ref bean="paymentService"/>

           

              </property>

           

              <property name="serviceInterface">

           

                <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

           </bean>

          你會(huì)發(fā)現(xiàn),除了Bean的名字(完全是任意的)和使用了BurlapServiceExporter以外,這個(gè)BeanhessianPaymentService是一樣的。配置Burlap服務(wù)和配置Hessian服務(wù)的其他方面也是一樣的,這也就包括了需要建一個(gè)URL處理器和DispatcherServletHessianBurlap解決了RMI頭疼的防火墻問題。

           

          四、使用HTTP invoker

          1.通過HTTP訪問服務(wù)

          訪問一個(gè)HTTP invoker服務(wù),你需要使用HttpInvokerProxyFactoryBean要讓支付服務(wù)作為一個(gè)HTTP invoker服務(wù)公開,得配置一個(gè)Bean,用HttpInvokerProxyFactoryBean來代理它,如下所示:

          <bean id="paymentService" class= "org.springframework.remoting.

           

                    ?httpinvoker.HttpInvokerProxyFactoryBean">

           

              <property name="serviceUrl">

           

                <value>http://${serverName}/${contextPath}/pay.service</value>

           

              </property>

           

              <property name="serviceInterface">

           

                <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

           </bean>

          serviceInterface屬性仍然用來表示這個(gè)支付服務(wù)所實(shí)現(xiàn)的接口;serviceUrl屬性仍然是用來表示遠(yuǎn)程支付服務(wù)的位置。由于HTTP invoker是基于HTTP的,如同HessianBurlap一樣,serviceUrl就能包含與HessianBurlap版本的Bean里一樣的URL

          2.把Bean作為HTTP服務(wù)公開

          使用HttpInvokerServiceExporterBean的方法輸出為遠(yuǎn)程方法,面的Bean的定義展示了如何把paymentService Bean作為一個(gè)遠(yuǎn)程的基于HTTP invoker的服務(wù)輸出:

          <bean id="httpPaymentService" class="org.springframework.remoting.

           

                  ?httpinvoker.HttpInvokerServiceExporter">

           

              <property name="service">

           

                <ref bean="paymentService"/>

           

              </property>

           

              <property name="serviceInterface">

           

                <value>com.springinaction.payment.PaymentService</value>

           

              </property>

           

          </bean>

          基于HTTP invoker的服務(wù),顧名思義,是基于HTTP的,就像HessianBurlap服務(wù)一樣。并且,也和HessianServiceExporterBurlapServiceExporter那樣,HttpInvokerServiceExporter也是一個(gè)SpringController。這就意味著你需要建立一個(gè)URL處理器,把HTTP URL映射到服務(wù)上:

          <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

           

              <property name="mappings">

           

                <props>

           

                   <prop key="/pay.service">httpPaymentService</prop>

           

                </props>

           

              </property>

           

           </bean>

          SpringHTTP invoker是作為一個(gè)兩全其美的遠(yuǎn)程調(diào)用解決方案出現(xiàn)的,把HTTP交流的簡(jiǎn)單性和Java內(nèi)置的對(duì)象序列化機(jī)制結(jié)合起來。這讓HTTP invoker服務(wù)成為一個(gè)引人注目的對(duì)RMI或是對(duì)Hessian/Burlap的替代品。

          要記住HTTP invoker有個(gè)重大的限制,它是一個(gè)只在Spring框架中提供的遠(yuǎn)程調(diào)用解決方案。這就意味著客戶端和服務(wù)器端都必須是使用Spring的應(yīng)用。

           

          五.使用EJB

          雖然Spring提供了大量的功能,讓POJO具有EJB的能力,但你或許不能總是享受在完全沒有EJB的項(xiàng)目上工作的奢侈。一方面,你可能會(huì)接觸一些其他系統(tǒng),它們的功能是通過無狀態(tài)的會(huì)話EJB開放出來的。另一方面,你可能被放在一個(gè)項(xiàng)目中,由于正統(tǒng)技術(shù)(或者可能是政治)的原因,你不得不寫EJB代碼。

          不管你的應(yīng)用是EJB客戶端,還是你必須寫EJB本身,你都不需要為了用EJB,完全放棄Spring帶來的好處。Spring有兩種方法提供對(duì)EJB的支持:

          ?   Spring能讓你在Spring的配置文件里,把EJB作為Bean來聲明。這樣,把EJB引用置入到其他Bean的屬性里就成為可能了,好像EJB就是另一個(gè)POJO

          ?   Spring能讓你寫EJB,讓EJB成為Spring配置的Bean的代理的工作。

           

          六、使用JAX-RPCWeb Service

          JAX-RPC基于XML的遠(yuǎn)程調(diào)用的Java APIJava APIs for XML-based remote procedure call的縮寫。這是一個(gè)口語化的詞,僅僅意味著JAX-RPCJava程序使用XML訪問遠(yuǎn)程服務(wù)的一種方式。特別地,這個(gè)服務(wù)是指用SOAPSimple Object Access Protocol)協(xié)議公開它們的功能的web service

           

          七、小結(jié)

          Spring提供了遠(yuǎn)程服務(wù)的支持,讓使用遠(yuǎn)程服務(wù)和使用常規(guī)的JavaBean一樣簡(jiǎn)單。

          在客戶端,Spring提供了代理工廠Bean,能讓你在Spring應(yīng)用中配置遠(yuǎn)程服務(wù)。不管是使用RMIHessianBurlapHTTP invokerEJB、還是Web service,你都可以把遠(yuǎn)程服務(wù)置入到你的應(yīng)用里,好像它們是POJO一樣。Spring甚至捕獲了所有拋出的RemoteException,并在發(fā)生異常的地方重新拋出運(yùn)行時(shí)RemoteAccessException,讓你的代碼從處理可能不可恢復(fù)的異常中解放出來。

              Spring的遠(yuǎn)程調(diào)用我不知什么時(shí)候才能用上,在這里在不太理解的基礎(chǔ)上對(duì)書作了簡(jiǎn)單的摘抄,先留個(gè)記號(hào)等以后用著了在回頭學(xué)習(xí)。

           

          posted on 2007-10-23 21:51 譚明 閱讀(1616) 評(píng)論(0)  編輯  收藏 所屬分類: Spring
          主站蜘蛛池模板: 东安县| 安吉县| 安泽县| 福海县| 新化县| 惠水县| 垫江县| 哈密市| 瑞丽市| 崇信县| 乐山市| 老河口市| 利津县| 长治县| 五台县| 图木舒克市| 盐源县| 普安县| 江阴市| 黔西| 吉首市| 浑源县| 巢湖市| 马山县| 深水埗区| 金坛市| 阳谷县| 博湖县| 广灵县| 灌云县| 武陟县| 丰宁| 新晃| 正安县| 彝良县| 浦江县| 霍林郭勒市| 荆州市| 泰和县| 石狮市| 长阳|