在Java開發中,咱們可使用多種遠程調用技術,如:javascript
注:關於Web Service能夠自行參考我以前寫過的相關文章(PS:www.zifangsky.cn/webservice)。在本篇文章中我主要介紹前面幾種方式的具體代碼實現php
遠程調用是客戶端應用和服務端之間的會話。在客戶端,它所須要的一些功能並不在該應用的實現範圍以內,因此應用要向能提供這些功能的其餘系統尋求幫助。一樣,遠程應用經過發佈服務將這些功能暴露給其餘系統使用java
從表面上看,RPC調用(遠程過程調用)相似於調用一個本地對象的某個方法。和本地方法調用相比二者都是同步操做,會阻塞調用代碼的執行,直到被調用的過程執行完畢web
本地方法調用是指同一個應用中的兩個代碼塊之間的執行流交換;RPC調用則是執行流從一個應用傳遞給另外一個應用的過程,理論上另外一個應用能夠部署在跨網絡的任意一臺其餘遠程機器上spring
Spring支持多種不一樣的RPC模型,包括RMI、Hessian/Burlap以及Spring自帶的Http Invoker。下面我將簡單介紹一下它們之間的異同點:api
RMI
:不考慮網絡限制時使用(PS:由於RMI使用任意端口來交互,有時沒法穿越防火牆)Hessian/Burlap
:考慮網絡限制時,經過HTTP訪問/發佈基於Java的服務。Hessian是基於二進制的遠程調用技術;而Burlap是基於XML的遠程調用技術Spring的HttpInvoker
:跟Hessian/Burlap實現的調用技術相似,可是不一樣的是Hessian/Burlap使用了私有的對象序列化機制,而Spring的Http Invoker則使用的是Java的序列化機制可是,無論選擇哪一種遠程調用模型咱們都會發現Spring提供了風格一致的支持。這意味着一旦理解了如何在Spring中配置和使用其中一種模型。那麼當咱們想要使用另一種模型的話,將會變得很是容易網絡
在全部的模型中,服務都做爲Spring所管理的bean配置到咱們的應用中。這是經過一個代理工廠bean實現的,這個bean可以把遠程服務像本地對象同樣裝配到其餘bean的屬性中去。客戶端向代理髮起調用,就像代理提供了這些服務同樣。代理表明客戶端與遠程服務進行通訊,由它負責處理鏈接的細節並向遠程服務發起調用。最後代理再返回遠程服務執行完成以後的結果,至此整個調用過程完成app
不管咱們開發的是使用遠程服務的代碼,仍是實現這些服務的代碼,或者二者兼而有之。在Spring中,使用遠程服務純粹是一個配置問題。咱們不須要編寫任何Java代碼就能夠支持遠程調用。咱們的服務bean也不須要關心它們是否參與了一個RPC(PS:任何傳遞給遠程調用的bean或從遠程調用返回的bean可能須要實現java.io.Serializable 接口)maven
package cn.zifangsky.rmi.service;
import java.util.Date;
public interface TestRMIService {
/** * 時間格式轉化服務 * @param date 時間 * @return String類型的時間字符串 */
public String formatDateService(Date date);
}複製代碼
package cn.zifangsky.rmi.service.impl;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.stereotype.Component;
import cn.zifangsky.rmi.service.TestRMIService;
@Component("testRMIServiceImpl")
public class TestRMIServiceImpl implements TestRMIService {
@Override
public String formatDateService(Date date) {
Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(date != null){
return format.format(date);
}else{
return "";
}
}
}複製代碼
從上面的代碼能夠看出,這些都是簡單的POJO,並無依賴其餘服務ide
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.zifangsky.rmi.service.impl" />
<bean id="testRMIService" class="org.springframework.remoting.rmi.RmiServiceExporter">
<!-- RMI服務名 -->
<property name="serviceName" value="testRMIService"/>
<!-- RMI具體服務實現類 -->
<property name="service" ref="testRMIServiceImpl" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.rmi.service.TestRMIService" />
<!-- 註冊端口 -->
<property name="registryPort" value="1099" />
</bean>
</beans>複製代碼
能夠看出,經過上面的XML配置導出了一個遠程服務,其地址是:rmi://127.0.0.1:1099/testRMIService
注:上面的service的值「testRMIServiceImpl」是上面的TestRMIServiceImpl類的一個實例,經過@Component註解生成並注入到這裏
上面定義好一個服務以後,咱們要想在客戶端(另外一個Web項目)中使用這個服務,那麼咱們確定是須要知道這個遠程方法所在的類、方法名等信息纔可以正常調用的。所以,接下來還須要將這些公共類打包成jar包放到客戶端的lib目錄才行
這一步能夠手動選擇導出的類,也可使用一個Ant腳原本自動化完成這一操做。我這裏使用的Ant腳本以下:
build_service.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project name="common_build" default="build_service" basedir=".">
<property name="targetdir" value="target"/>
<property name="classbase" value="WebContent/WEB-INF/classes/"/>
<property name="xpf.copy.info" value="true"/>
<property name="model.name" value="test"/>
<property name="env.name" value="dev"/>
<target name="build_service">
<jar destfile="${basedir}/target/zifangsky_${model.name}_RMIService_api.jar">
<fileset dir="${classbase}">
<include name="cn/zifangsky/**/model/*.class" />
<include name="cn/zifangsky/**/model/**/*.class" />
<include name="cn/zifangsky/**/service/*.class" />
<include name="cn/zifangsky/**/common/*.class" />
<include name="cn/zifangsky/**/common/**/*.class" />
</fileset>
</jar>
</target>
</project>複製代碼
最後將生成的zifangsky_test_RMIService_api.jar放到客戶端的lib目錄下便可
context_rmi_client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.zifangsky.rmi.service" />
<bean id="testRMIServiceClient" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<!-- 服務地址 -->
<property name="serviceUrl" value="rmi://127.0.0.1:1099/testRMIService" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.rmi.service.TestRMIService" />
</bean>
</beans>複製代碼
這裏的代碼很簡單,本身看註釋就明白了
package cn.zifangsky.test.rmi;
import java.util.Date;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zifangsky.rmi.service.TestRMIService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml","classpath:/context/context_rmi_client.xml"})
public class TestRMI {
@Resource(name="testRMIServiceClient")
private TestRMIService testRMIService;
@Test
public void testFormatDate(){
Date date = new Date();
System.out.println("格式化後的時間是: " + testRMIService.formatDateService(date));
}
}複製代碼
最後輸出以下:
格式化後的時間是: 2017-01-08 15:55:49複製代碼
從控制檯的輸出結果來看,這裏的客戶端代碼的確調用了遠程服務實現了時間格式的轉化。到此,關於RMI的配置和使用就結束了,下面介紹的其餘的幾種遠程調用的配置和使用跟RMI相似,所以我就簡單敘述了
Hessian和Burlap是Caucho Technology提供的兩種基於HTTP的輕量級遠程服務解決方案。藉助於儘量簡單的API和通訊協議,它們都致力於簡化Web服務。
關於如何選擇使用Hessian仍是Burlap發佈服務,很大程度上,它們是同樣的。惟一的區別在於Hessian的消息是二進制的,而Burlap的消息是XML格式。由於Hessian的消息是二進制的,因此它在帶寬上更具優點。可是若是咱們更注重可讀性(如出於調試的目的)或者咱們的應用須要與沒有Hessian實現的語言交互,那麼Burlap的XML消息將會是更好的選擇
下載地址:hessian.caucho.com
package cn.zifangsky.hessian.service;
public interface TestHessianService {
/** * 測試接口 * @return */
public String sayHello();
}複製代碼
package cn.zifangsky.hessian.service.impl;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.stereotype.Component;
import cn.zifangsky.hessian.service.TestHessianService;
@Component("testHessianServiceImpl")
public class TestHessianServiceImpl implements TestHessianService{
@Override
public String sayHello() {
Date date = new Date();
Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return "Hello,this's TestHessianService ---Time: " + format.format(date);
}
}複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.zifangsky.hessian.service.impl" />
<bean id="/testHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter">
<!-- Hessian具體服務實現類 -->
<property name="service" ref="testHessianServiceImpl" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.hessian.service.TestHessianService" />
</bean>
</beans>複製代碼
配置web.xml將全部的Hessian服務單獨發佈到一個目錄:
<servlet>
<servlet-name>hessian</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:context/context_hessian.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hessian</servlet-name>
<url-pattern>/hessian/*</url-pattern>
</servlet-mapping>複製代碼
所以,上面的那個測試服務的地址是:http://localhost:9180/RMIServerDemo/hessian/testHessianService
操做步驟同上面的RMI的操做,略
context_hessian_client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="testHessianServiceClient" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<!-- 服務地址 -->
<property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/hessian/testHessianService" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.hessian.service.TestHessianService" />
</bean>
</beans>複製代碼
package cn.zifangsky.test.hessian;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zifangsky.hessian.service.TestHessianService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml","classpath:/context/context_hessian_client.xml"})
public class TestHessian {
@Resource(name="testHessianServiceClient")
TestHessianService testHessianService;
@Test
public void sayHello(){
System.out.println(testHessianService.sayHello());
}
}複製代碼
最後輸出以下:
Hello,this's TestHessianService ---Time: 2017-01-08 16:48:25複製代碼
下載地址:repo1.maven.org/maven2/edu/…
接口TestBurlapService.java:
package cn.zifangsky.burlap.service;
public interface TestBurlapService {
/** * 測試接口 * @return */
public String sayHello();
}複製代碼
其實現類TestBurlapServiceImpl.java:
package cn.zifangsky.burlap.service.impl;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.stereotype.Component;
import cn.zifangsky.burlap.service.TestBurlapService;
@Component("testBurlapServiceImpl")
public class TestBurlapServiceImpl implements TestBurlapService{
@Override
public String sayHello() {
Date date = new Date();
Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return "Hello,this's TestBurlapService ---Time: " + format.format(date);
}
}複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.zifangsky.burlap.service.impl" />
<bean id="/testBurlapService" class="org.springframework.remoting.caucho.BurlapServiceExporter">
<!-- Burlap具體服務實現類 -->
<property name="service" ref="testBurlapServiceImpl" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.burlap.service.TestBurlapService" />
</bean>
</beans>複製代碼
對應的web.xml配置:
<servlet>
<servlet-name>burlap</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:context/context_burlap.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>burlap</servlet-name>
<url-pattern>/burlap/*</url-pattern>
</servlet-mapping>複製代碼
所以,上面的那個測試服務的地址是:http://localhost:9180/RMIServerDemo/burlap/testBurlapService
操做步驟同上面的RMI的操做,略
context_burlap_client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="testBurlapServiceClient" class="org.springframework.remoting.caucho.BurlapProxyFactoryBean">
<!-- 服務地址 -->
<property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/burlap/testBurlapService" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.burlap.service.TestBurlapService" />
</bean>
</beans>複製代碼
package cn.zifangsky.test.burlap;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zifangsky.burlap.service.TestBurlapService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml","classpath:/context/context_burlap_client.xml"})
public class TestBurlap {
@Resource(name="testBurlapServiceClient")
private TestBurlapService testBurlapService;
@Test
public void sayHello(){
System.out.println(testBurlapService.sayHello());
}
}複製代碼
最後輸出以下:
Hello,this's TestBurlapService ---Time: 2017-01-08 17:03:47複製代碼
接口TestHttpInvokerService.java:
package cn.zifangsky.httpinvoker.service;
public interface TestHttpInvokerService {
/** * 測試接口 * @return */
public String sayHello();
}複製代碼
其實現類TestHttpInvokerServiceImpl.java:
package cn.zifangsky.httpinvoker.service.impl;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.stereotype.Component;
import cn.zifangsky.httpinvoker.service.TestHttpInvokerService;
@Component("testHttpInvokerServiceImpl")
public class TestHttpInvokerServiceImpl implements TestHttpInvokerService {
@Override
public String sayHello() {
Date date = new Date();
Format format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return "Hello,this's TestHttpInvokerService ---Time: " + format.format(date);
}
}複製代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="cn.zifangsky.httpinvoker.service.impl" />
<bean id="/testHttpInvokerService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<!-- Http Invoker具體服務實現類 -->
<property name="service" ref="testHttpInvokerServiceImpl" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.httpinvoker.service.TestHttpInvokerService" />
</bean>
</beans>複製代碼
對應的web.xml配置:
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:context/context_invoker.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/invoker/*</url-pattern>
</servlet-mapping>複製代碼
所以,上面的那個測試服務的地址是:http://localhost:9180/RMIServerDemo/invoker/testHttpInvokerService
操做步驟同上面的RMI的操做,略
context_httpinvoker_client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="testHttpInvokerServiceClient" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<!-- 服務地址 -->
<property name="serviceUrl" value="http://localhost:9180/RMIServerDemo/invoker/testHttpInvokerService" />
<!-- 服務調用接口 -->
<property name="serviceInterface" value="cn.zifangsky.httpinvoker.service.TestHttpInvokerService" />
</bean>
</beans>複製代碼
(5)測試:
package cn.zifangsky.test.httpinvoker;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zifangsky.httpinvoker.service.TestHttpInvokerService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/context/context.xml","classpath:/context/context_httpinvoker_client.xml"})
public class TestHttpInvoker {
@Resource(name="testHttpInvokerServiceClient")
private TestHttpInvokerService testHttpInvokerService;
@Test
public void sayHello(){
System.out.println(testHttpInvokerService.sayHello());
}
}複製代碼
最後輸出以下:
Hello,this's TestHttpInvokerService ---Time: 2017-01-08 17:09:51複製代碼
至此,關於基於Spring的遠程過程調用(RPC)的四種實現方式的介紹就所有結束了。從上面的代碼能夠看出,因爲Spring的封裝,這幾種服務的發佈和實現步驟都是很相似的,而且咱們實際須要作的工做也變得很是簡單,咱們很大程度上只須要關注咱們的具體業務邏輯就好了