在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的封装,这几种服务的发布和实现步骤都是很相似的,而且咱们实际须要作的工做也变得很是简单,咱们很大程度上只须要关注咱们的具体业务逻辑就好了