概述
本章主要讲述了如何在Spring中使用远程服务,如RMI,Caucho的Hessian和Burlap,HttpInvoker,Web Service等。
进阶阅读:java 几种远程服务调用协议的比较
一、Spring远程调用概览
远程调用是客户端应用和服务端应用之间的会话。在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向能提供这些功能的其他系统寻求帮助。而远程应用通过远程服务暴露这些功能。
RPC(remote-procedure call. RPC)远程过程调用: 就是执行流从一个应用传递给另一个应用,理论上另一个应用部署在跨网络的一台远程机器上。
Spring支持的多种不同的RPC模型
RPC模型 | 使用场景 |
远程方法调用(RMI) | 不考虑网络限制时(例如防火墙),访问/发布基于Java的服务 |
Hessian或Burlap | 考虑网络限制时,通过HTTP访问/发布基于Java的服务。Hessian是二进制协议.而Burlap是基于XML的 |
HTTP invoker | 考虑网络限制,并希望使用基于XML或专有的序列化机制实现Java序列化时,访问/发布基于Spring的服务 |
JAX-RPC和JAX-WS的WebService | 访问/发布平台独立的、基于SOAP的Web服务 |
注:SOAP为简单对象访问协议,是基于XML的简易协议,可使应用程序在Http之上进行信息交换。即用于访问网络服务的协议。
二、使用RMI
1、服务端发布RMI服务
如果你曾经创建过RMI服务,应该会知道这会涉及如下几个步骤:
- 编写一个服务实现类,类中的方法必须抛出java.rmi.RemoteException异常;
- 创建一个继承于java.rmi.Remote的服务接口;
- 运行RMI编译器(rmic),创建客户端stub类和服务端skeleton类;
- 启动一个RMI注册表,以便持有这些服务;
- 在RMI注册表中注册服务。
以传统方式发布RMI服务不仅需要做上诉那么多的工作,而且会抛出相当多的RemoteException和MalformedURLException异常。而这些异常通常是无法修复的,但是我们仍然需要写很多的样板代码去catch它。幸运的是,Spring提供了更简单的方式来发布RMI服务,不用再编写那些需要抛出RemoteException异常的特定RMI类,只需要简单地编写实现服务功能的POJO就可以了,Spring会处理剩余的事项。
1、1 首先我们在服务端创建一个需要发布的服务接口:
public interface IHelloService {
String getMsg();
}
1、2 该服务接口的实现类:
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements IHelloService {
@Override
public String getMsg() {
return "Hello World.(From RMI server)";
}
}
1、3 配置RmiServiceExporter(RmiServiceExporter 可以用来把任意Spring管理的bean发布为RMI服务)
RmiServiceExporter的实现原理: 通过把bean包装在一个适配器类中,然后适配器类被绑定到RMI注册表中,并且代理到服务类的请求——在本例中服务类也就是HelloServiceImpl.
配置RmiServiceExporter的具体细节如下:
import com.scb.h2demo.service.HelloServiceImpl;
import com.scb.h2demo.service.IHelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiServiceExporter;
@Configuration
public class RMIConfig {
//服务端使用RMI发布服务配置
@Bean
public RmiServiceExporter helloServiceExporter(HelloServiceImpl helloService){
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
//将服务端需要发布的服务接口实现类配置到RMI服务中
rmiServiceExporter.setService(helloService);
//设置RMI服务名(客户端依据此服务名来获取远程服务)
rmiServiceExporter.setServiceName("HelloService");
//将远程接口设置为RMI服务接口
rmiServiceExporter.setServiceInterface(IHelloService.class);
//为RMI服务端远程对象注册表设置端口
rmiServiceExporter.setRegistryPort(9090);
return rmiServiceExporter;
}
}
这就是我们使用Spring把某个bean转变为RMI服务所需要做的全部工作。接下来,让我们看看客户端如何装配RMI服务。
2、客户端装配RMI服务
2、1 我们首先需要把服务端发布的服务接口类Copy到我们的项目下
public interface IHelloService {
String getMsg();
}
2、2 配置RmiProxyFactoryBean(Spring的RmiProxyFactoryBean是一个工厂bean,该bean可以为RMI服务创建代理。)
下图展示了客户端和RMI的交互: (RmiProxyFactoryBean生成一个代理对象,该对象代表客户端来负责与远程的RMI服务进行通讯。客户端通过服务的接口与代理进行交互——所以需要2、1步骤,就如同远程服务就是一个本地的POJO)
配置RmiProxyFactoryBean的具体细节如下:
import com.spring.security.springsecurity.service.IHelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;
@Configuration
public class RMIConfig {
@Bean
public RmiProxyFactoryBean helloService(){
RmiProxyFactoryBean rmiProxyFactoryBean=new RmiProxyFactoryBean();
//url书写规则: rmi://registryHost:registryPort/ServiceName
//registryHost为服务端项目运行地址,如果在本地跑,就是localhost
//registryPort为服务端的远程对象注册表的端口号
//ServiceName为服务端发布的服务名
rmiProxyFactoryBean.setServiceUrl("rmi://localhost:9090/HelloService");
//这里相当于使用接口注入,把远程的HelloServiceImpl实现类注入到客户端的IHelloService接口中。
//这样我们就可以通过操作客户端的服务接口,来间接调用服务端的具体服务。
rmiProxyFactoryBean.setServiceInterface(IHelloService.class);
//当连接失败时是否刷新远程调用stub
rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
return rmiProxyFactoryBean;
}
}
2、3 使用服务
@Autowired
IHelloService helloService;
接着直接调用IHelloService中的方法即可。
@GetMapping(value = "/RmiClient")
@ResponseBody
public String rmiMsg(){
return helloService.getMsg();
}
3、如何发布/装配多个服务
3、1 服务端发布多个服务
发布多个服务其实只需要配置多个RmiServiceExporter即可。如:下面例子在项目使用H2Database+Druid连接池+Spring Data JPA+Ehcache实现CRUD操作的基础上,将CRUD功能发布为服务。
@Bean
public RmiServiceExporter staffServiceExporter(StaffServiceImpl staffService){
RmiServiceExporter rmiServiceExporter = new RmiServiceExporter();
//将远程接口实现类对象设置到RMI服务中
rmiServiceExporter.setService(staffService);
//设置RMI服务名,RMI客户端依据此服务名来获取远程接口实现类对象引用
rmiServiceExporter.setServiceName("StaffService");
//将远程接口设置为RMI服务接口
rmiServiceExporter.setServiceInterface(IStaffService.class);
//为RMI服务端远程对象注册表设置端口
rmiServiceExporter.setRegistryPort(9090);
return rmiServiceExporter;
}
3、2 客户端装配多个服务
同理,客户端装配多个服务也只需要配置多个RmiProxyFactoryBean(当然,IStaffService还是需要复制到客户端的,这个所放路径可随意)。
@Bean
public RmiProxyFactoryBean staffService(){
RmiProxyFactoryBean rmiProxyFactoryBean=new RmiProxyFactoryBean();
rmiProxyFactoryBean.setServiceUrl("rmi://localhost:9090/StaffService");
rmiProxyFactoryBean.setServiceInterface(IStaffService.class);
rmiProxyFactoryBean.setRefreshStubOnConnectFailure(true);
return rmiProxyFactoryBean;
}
需要注意的是,如果客户端在装配远程服务时,需要远程服务的pojo类,需要把服务端的POJO类复制到客户端的相同路径下。比如此处的Staff对象。
三、使用Hessian和Burlap
1、使用Hessian和Burlap的所需依赖包
<!-- Hessian依赖包 -->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
</dependency>
<!-- Burlap依赖包 -->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>burlap</artifactId>
</dependency>
2、服务端发布服务(以Hessian为例子)
package com.scb.h2demo.config;
import com.scb.h2demo.service.HessianServiceImpl;
import com.scb.h2demo.service.IHessianService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianServiceExporter;
@Configuration
public class HessianConfig {
/**
* 服务端配置
* @param hessianService
* @return
*/
@Bean("/hessian.service")
public HessianServiceExporter hessianServiceExporter(HessianServiceImpl hessianService){
HessianServiceExporter exporter=new HessianServiceExporter();
exporter.setService(hessianService);
exporter.setServiceInterface(IHessianService.class);
return exporter;
}
}
与RmiServiceExporter不同的是,Hessian和Burlap不需要配置serviceName属性。
@Bean("/hessian.service"):加斜杠会被SpringBoot自动暴露服务地址并发布服务。
3、客户端装配服务
package com.spring.security.springsecurity.config;
import com.spring.security.springsecurity.service.IHessianService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianProxyFactoryBean;
@Configuration
public class HessianConfig {
@Bean
public HessianProxyFactoryBean hessianProxyFactoryBean(){
HessianProxyFactoryBean hessianProxyFactoryBean=new HessianProxyFactoryBean();
hessianProxyFactoryBean.setServiceUrl("http://localhost:8080/hessian.service");
hessianProxyFactoryBean.setServiceInterface(IHessianService.class);
return hessianProxyFactoryBean;
}
}
四、使用Spring的HttpInvoker
其实在Spring中使用远程服务都是大同小异的,我们使用HttpInvoker也是这样,服务端发布服务配置如下:
package com.scb.h2demo.config;
import com.scb.h2demo.service.HttpInvokerServiceImpl;
import com.scb.h2demo.service.IHttpInvokerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
@Configuration
public class HttpInvokerConfig {
@Bean("/httpInvoker.service")
public HttpInvokerServiceExporter httpInvokerServiceExporter(HttpInvokerServiceImpl service){
HttpInvokerServiceExporter exporter=new HttpInvokerServiceExporter();
exporter.setService(service);
exporter.setServiceInterface(IHttpInvokerService.class);
return exporter;
}
}
客户端装配服务配置如下:
package com.spring.security.springsecurity.config;
import com.spring.security.springsecurity.service.IHttpInvokerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
@Configuration
public class HttpInvokerConfig {
@Bean
public HttpInvokerProxyFactoryBean httpInvokerProxyFactoryBean(){
HttpInvokerProxyFactoryBean proxyFactoryBean=new HttpInvokerProxyFactoryBean();
proxyFactoryBean.setServiceUrl("http://localhost:8080/httpInvoker.service");
proxyFactoryBean.setServiceInterface(IHttpInvokerService.class);
return proxyFactoryBean;
}
}
五、发布和使用Web Service
Web Service是一种跨编程语言和跨操作系统平台的远程调用技术。