在一個程序中就像調用本地中的方法同樣調用另一個遠程程序中的方法,可是整個過程對本地徹底透明,這就是遠程調用。spring已經可以很是成熟的完成該項功能了。html
客戶端調用本地接口中的一個方法調用的時候將會被客戶端代理攔截,並向遠程服務器發起一個servlet請求(服務器中的web.xml文件中專門配置了該Servlet),服務器接受請求,並根據spring配置文件和請求的url找到對應的「導出器」,導出器將pojo轉換成控制器controller,並最終完成請求的任務,並將結果以流的形式返回到客戶端,客戶端由此拿到額執行結果的數據。java
服務端和客戶端之間通訊使用的是序列化對象類型,以後會說明該問題。git
實現了遠程調用的程序可以對外提供一種「服務」,其它程序可以沒必要重寫它的功能模塊就可以實現某個功能,其它程序所須要作的僅僅只是一句調用的代碼而已。這樣作可以極大的提升開發效率。github
以當前的數據採集系統爲例,已經實現了對數據的統計功能,可是其它網站想要重用你的代碼就很困難了,好比其它網站在咱們的數據採集系統中新建了調查,調查完畢以後其它網站想要獲取統計結果數據並在本身的應用程序中進行統計,這時候獲取統計數據就是比較重要的事情了web
(1).在數據採集系統中使用「導出到XML」文件或者「導出到Excel文件」的功能將數據導出到文件中,而後在本身的應用程序中對這些數據進行分析。使用這種方式不只須要分析數據,還要本身進行統計代碼的書寫,是一種很是笨的方法。spring
(2).使用數據採集系統提供的公共接口,直接遠程調用對應接口中的方法,實現過程對本地透明。獲取的數據是已經徹底封裝好的數據,直接使用便可。可是使用這種方式須要兩個程序都使用spring環境(java環境),並且客戶端須要擁有接口和相關bean的class文件。express
綜合以上兩方面使用第二種方式是更爲理想的方式,可是缺點也是很明顯的,那就是強制兩端都使用spring環境。可以有折中方案呢?即可以有相似於遠程調用的功能,可是不用強制限制客戶端使用spring或者java環境,解決方案就是使用WebService,使用WebService雖然解決了以上的矛盾問題,可是它自己也有缺點,那就是效率比較低。從中能夠看出使用遠程調用和使用WebService各有優劣之處,應當根據不一樣的環境使用不一樣的方法。apache
當兩端程序都是用Spring環境的狀況下,使用遠程調用提供外部服務;當其中有一端不使用Spring的時候,使用WebServiceapi
源代碼地址:https://github.com/kdyzm/day78_spring_rpc_demo/blob/master/readme.md瀏覽器
首先將Spring的相關jar包加上:特別是下面強調部分的jar包,是遠程實現遠程調用的核心jar包。
com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.apache.commons.codec-1.3.0.jar
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.aspectj.tools-1.6.6.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
com.springsource.org.quartz-1.6.2.jar
commons-lang-2.5.jar
log4j.jar
org.springframework.aop-3.1.0.RELEASE.jar
org.springframework.asm-3.1.0.RELEASE.jar
org.springframework.aspects-3.1.0.RELEASE.jar
org.springframework.beans-3.1.0.RELEASE.jar
org.springframework.context-3.1.0.RELEASE.jar
org.springframework.context.support-3.1.0.RELEASE.jar
org.springframework.core-3.1.0.RELEASE.jar
org.springframework.expression-3.1.0.RELEASE.jar
org.springframework.jdbc-3.1.0.RELEASE.jar
org.springframework.orm-3.1.0.RELEASE.jar
org.springframework.transaction-3.1.0.RELEASE.jar
org.springframework.web-3.1.0.RELEASE.jar
org.springframework.web.servlet-3.1.0.RELEASE.jar
slf4j-api-1.5.8.jar
slf4j-log4j12.jar
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> 3 <display-name>RPC_Server</display-name> 4 <servlet> 5 <servlet-name>dispatcherServlet</servlet-name> 6 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 7 <init-param> 8 <param-name>contextConfigLocation</param-name> 9 <param-value>classpath:applicationContext.xml</param-value> 10 </init-param> 11 </servlet> 12 <servlet-mapping> 13 <servlet-name>dispatcherServlet</servlet-name> 14 <url-pattern>*.service</url-pattern> 15 </servlet-mapping> 16 <welcome-file-list> 17 <welcome-file>index.html</welcome-file> 18 <welcome-file>index.htm</welcome-file> 19 <welcome-file>index.jsp</welcome-file> 20 <welcome-file>default.html</welcome-file> 21 <welcome-file>default.htm</welcome-file> 22 <welcome-file>default.jsp</welcome-file> 23 </welcome-file-list> 24 </web-app>
1 package com.kdyzm.rpc.server; 2 3 public interface WelcomeService { 4 public String say(String name); 5 }
1 package com.kdyzm.rpc.server; 2 3 public class WelcomServiceImpl implements WelcomeService{ 4 5 @Override 6 public String say(String name) { 7 System.out.println("你好,"+name); 8 return name; 9 } 10 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd 6 http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd 7 http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd 8 http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd"> 9 <bean id="welcomeService" class="com.kdyzm.rpc.server.WelcomServiceImpl"></bean> 10 <bean class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" name="/ws.service"> 11 <property name="serviceInterface"> 12 <value>com.kdyzm.rpc.server.WelcomeService</value> 13 </property> 14 <property name="service" ref="welcomeService"></property> 15 </bean> 16 </beans>
將服務端的程序發佈到tomcat,在瀏覽器上訪問http://localhost:8080/RPC_Server/ws.service,觀察瀏覽器顯示結果。
若是顯示這樣的信息表示服務端已經配置成功了。爲何會是這樣呢?由於服務端提供的不是http服務,而是應用程序的服務,必須使用正確的客戶端才能正確的訪問。
先作好7.中的客戶端程序,而後進行下面的流程。
之因此會出現這種現象,是由於遠程調用的時候客戶端和服務端之間的數據傳遞使用的是序列化對象的形式,可使用eclipse自帶的TCP/IP監視器查看,關於它的用法就是將客戶端的請求端口改爲監視端口,該監視器會將從指定監聽端口上監聽到的流量轉發到實際請求的端口號,服務器響應的時候則反過來。
最終捕獲到到的請求和相應信息:
將RPC_Server中的依賴類庫完完整整的拷貝到RPC_Client中。而後將接口WelcomeService也拷貝到該項目中,可是實現類WelcomeServiceImpl就不要拷貝到項目中了。
實際上客戶端的配置文件只須要配置一處地方就能夠了,配置的是客戶端代理。
1 <bean id="wsClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> 2 <property name="serviceInterface"> 3 <value>com.kdyzm.rpc.client.WelcomeService</value> 4 </property> 5 <property name="serviceUrl"> 6 <value>http://localhost:8080/RPC_Server/ws.service</value> 7 </property> 8 </bean>
該配置實際上創建了本地接口和遠程Servlet之間的一個映射關係,當在本地訪問配置的Service接口的時候,代理會向遠程服務器發起遠程訪問,訪問的正是以前在服務器中配置的servlet訪問地址。
完整的配置爲:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/beans/spring-beans-2.5.xsd 6 http://www.springframework.org/schema/context file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/context/spring-context-2.5.xsd 7 http://www.springframework.org/schema/aop file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/aop/spring-aop-2.5.xsd 8 http://www.springframework.org/schema/tx file:///D:\程序\java\Spring\spring-framework-4.2.1\spring-framework-4.2.1.RELEASE\schema/tx/spring-tx-2.5.xsd"> 9 <!-- 配置客戶端代理 --> 10 <bean id="wsClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> 11 <property name="serviceInterface"> 12 <value>com.kdyzm.rpc.client.WelcomeService</value> 13 </property> 14 <property name="serviceUrl"> 15 <value>http://localhost:8080/RPC_Server/ws.service</value> 16 </property> 17 </bean> 18 </beans>
1 public class Main{ 2 public static void main(String args[]){ 3 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); 4 WelcomeService service=(WelcomeService) context.getBean("wsClient"); 5 service.say("狗蛋"); 6 } 7 }
這裏是顯示的是tomcat打印的結果,也就是說客戶端已經正確訪問了服務端的程序。調用了服務端的方法。
到這裏spring遠程調用的實現已經完成了。
已經有了以前的例子,將當前的數據採集系統中的SurveyService配置成爲接口,實現spring的遠程調用也就很是簡單了。
1 <!-- 配置spring遠程調用使用的分發器 --> 2 <servlet> 3 <servlet-name>dispatcherServlet</servlet-name> 4 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 5 <init-param> 6 <param-name>contextConfigLocation</param-name> 7 <param-value>classpath:spring/applicationContext.xml</param-value> 8 </init-param> 9 </servlet> 10 <servlet-mapping> 11 <servlet-name>dispatcherServlet</servlet-name> 12 <url-pattern>*.service</url-pattern> 13 </servlet-mapping>
服務接口是SurveyService,實現類是SurveyServiceImpl,在項目中以前都已經寫好了,並且不須要使用XML的形式歸入Spring容器管理,它們都是用註解的形式歸入了Spring容器中並進行了配置。
1 <!-- 配置Spring遠程調用服務端 --> 2 <bean name="/surveyService.service" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> 3 <property name="serviceInterface" value="com.kdyzm.service.SurveyService"></property> 4 <property name="service" ref="surveyService"></property> 5 </bean>
到此爲止,服務端已經配置完成。
方法:項目右鍵->build path->config build path->projects,選擇LSN_surveypark項目,這樣LSN_surveypark項目就變成了RPC_Client的依賴項目了。
1 <bean id="surveyClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> 2 <property name="serviceInterface"> 3 <value>com.kdyzm.service.SurveyService</value> 4 </property> 5 <property name="serviceUrl"> 6 <value>http://localhost:8080/LSN_ServyPark/surveyService.service</value> 7 </property> 8 </bean>
這裏以獲取全部可用的調查爲例進行測試,注意各個Bean對象最好都是先實現Serialize接口,這樣才能進行網絡傳輸。不然的話服務端首先會報錯,由於沒法序列化。
測試代碼:
1 package com.kdyzm.rpc.client; 2 3 import java.util.Collection; 4 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 import com.kdyzm.domain.Page; 9 import com.kdyzm.domain.Question; 10 import com.kdyzm.domain.Survey; 11 import com.kdyzm.service.SurveyService; 12 13 public class Main { 14 public static void main(String[] args) { 15 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); 16 /*WelcomeService service=(WelcomeService) context.getBean("wsClient"); 17 service.say("狗蛋");*/ 18 SurveyService surveyService=(SurveyService) context.getBean("surveyClient"); 19 Collection<Survey>surveys=surveyService.getAllAvailableSurveys(); 20 for(Survey survey:surveys){ 21 System.out.println(survey.getSurveyId()+"_"+survey.getTitle()); 22 //使用transient關鍵字實現對字段的屏蔽(不序列化) 23 if(survey.getPages()!=null) 24 for(Page page:survey.getPages()){ 25 System.out.println("\t"+page.getPageId()+"_"+page.getOrderNo()); 26 for(Question question:page.getQuestions()){ 27 System.out.println("\t\t"+question.getQuestionId()+"_"+question.getTitle()); 28 } 29 } 30 } 31 } 32 }
運行程序,顯示查看控制檯顯示的結果信息。
顯示的結果是一共有8個可用的調查,這些打印結果都是封裝到對象以後傳輸過來的。以前的錯誤信息提示就忽略便可,是jar包衝突致使的問題,不影響測試結果。