1.简介
前面说过RMI远程服务使用的是Java标准的对象私有序列化机制,但很难穿越防火墙,而Hessian/Burlap远程服务是基于HTTP的服务,所以不受防火墙限制,但采用了私有的对象序列化机制。Spring框架的
HttpInvoker服务
综合了RMI服务和Hessian服务的优点,解决了防火墙问题以及采用了Java标准的对象系列化方式。
Spring整合Hessian远程服务调用与Spring整合HttpInvoker服务调用方式是一样的,仅是在服务端将HessianServiceExporter
替换为org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
,客户端只要将HessianProxyFactoryBean
换成org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
。因此本文只介绍Java类配置方式,不再介绍XML配置方式。如果想了解XML配置,可参照Hessian远程服务调用方式(Spring远程服务----Hessian和Burlap实现远程调用)。
2.介绍
HttpInvoker远程服务调用中一些重要的类:
- DispatcherServlet/HttpRequestHandlerServlet:负责将符合配置URL的访问路径的请求转给URL控制器,最终被
HttpInvokerServiceExporter
处理。
- HttpInvokerServiceExporter:HttpInvokerServiceExporter
是一个Spring MVC控制器,可以接收客户端请求,并将这些请求转换成对Spring bean的方法调用。
- HttpInvokerProxyFactoryBean:
HttpInvokerProxyFactoryBean
是一个代理工厂,用于生成一个代理,该代理使用Spring特有的基于Http协议进行远程通信。
项目介绍:
——Common项目
- 服务端和客户端都要使用接口和实体类,将实体类和接口定义到公共项目Common中,打成jar包,在客户端和服务端添加jar包依赖。
<!--common(实体和接口)的jar依赖-->
<dependency>
<groupId>com.corp</groupId>
<artifactId>common</artifactId>
<version>1.0.0</version>
</dependency>
——HttpInvoker服务端:服务端,实现common中的接口,提供HttpInvoker远程服务。
——HttpInvoker客户端:客户端,将远程服务HttpInvoker的代理注入,进行服务的调用。
2.1 Common
- 实体类
Account
为了远程传输实体类,实体类要实现Serializable
接口。
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Account [name=" + name + "]";
}
}
- 接口
AccoutService
public interface AccountService {
public void insertAccount(Account account);
public List<Account> getAccounts(String name);
}
2.2 HttpInvokerServer
步骤一:编写接口实现类AccountServiceImpl
@Service
public class AccountServiceImpl implements AccountService{
public void insertAccount(Account account) {
return;
}
public List<Account> getAccounts(String name) {
List<Account> accounts = new ArrayList<>();
Account account = new Account();
account.setName("DreamTech1113");
accounts.add(account);
return accounts;
}
}
步骤二:类配置方式创建DispatcherServlet
和ContextLoadListener
。
在Servlet3.0环境中,容器会在类路径中查找javax.servlet.ServletContainInitializer接口的类,如果能发现,就会用它来配置Servlet容器,Spring提供了该接口的实现,名称为SpringServletContainInitializer,这个类又会查找实现WebApplicationInitializer的类并将配置的任务交给他们来完成。Spring3.2中引入了一个便利的WebApplicationInitializer基础实现,即AbstractAnnotationConfigDispatcherServletInitializer。因此部署到Servlet3.0时,会以AbstractAnnotationConfigDispatcherServletInitializer类或者扩展类来配置Servlet上下文。
AbstractAnnotationConfigDispatcherServletInitializer
会同时创建DispatcherServlet
和ContextLoadListener
.继承此类重写三个方法:
- getServletConfigClasses():方法返回的带有@Configuration的注解的类会用来定义
DispatcherServlet
应用上下文中的bean。 - getRootConfigClasses():方法返回的带有@Configuration注解的类将用来配置
ContextLoadListener
创建的应用上下文中的bean。 - getServletMappings():为DispatcherServlet配置Servlet映射。
public class AccountWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {RootConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[] {"/httpinvoker/*"};
}
}
@Configuration
//开启包扫描
@ComponentScan(basePackages="com.spring")
public class WebConfig {
@Bean
public HttpInvokerServiceExporter accountExporter(AccountService accountService) {
HttpInvokerServiceExporter httpInvokerExporter = new HttpInvokerServiceExporter();
httpInvokerExporter.setService(accountService);
httpInvokerExporter.setServiceInterface(AccountService.class);
return httpInvokerExporter;
}
@Bean
public HandlerMapping httpInvokerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
Properties mappings = new Properties();
//当访问路径是/httpInvoker/accountExporter时,Servlet会请求转给accountExporter方法
mappings.setProperty("/accountExporter","accountExporter");
mapping.setMappings(mappings);
return mapping;
}
}
@Configuration
@ComponentScan(basePackages="com.spring")
public class RootConfig {
}
将服务端项目HttpInvokerServer放到tomcat中,启动tomcat(端口是8080)。
2.3 HttpInvokerConsumer
为了方便客户端启动一个tomcat,我们使用SpringBoot项目,将客户端项目的tomcat端口修改为8889
,在src/main/resources
目录下面新建文件application.properties
,内容为
server.port=8889
步骤一:编写服务调用层HttpInvokerContoller
。
@RestController
public class HttpInvokerController {
private AccountService accountService;
@Autowired
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
@RequestMapping("/home")
public List<Account> getAccount() {
List<Account> accounts = accountService.getAccounts("name");
return accounts;
}
}
步骤二:Java类配置HttpInvoker
客户端代理。
@Configuration
public class HttpInvokerClientConfiguration {
@Bean
public HttpInvokerProxyFactoryBean httpInvokerProxy() {
HttpInvokerProxyFactoryBean httpInvokerProxy = new HttpInvokerProxyFactoryBean();
//标识服务所实现的接口
httpInvokerProxy.setServiceInterface(AccountService.class);
//远程服务的地址
httpInvokerProxy.setServiceUrl("http://localhost:8080/httpinvoker_server/httpinvoker/accountExporter");
return httpInvokerProxy;
}
}
步骤三:SpringBoot
项目启动类。
@SpringBootApplication
@ComponentScan(basePackages="com.spring")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.测试
页面输入网址:http://localhost:8889/home
,结果:
[{“name”:“DreamTech1113”}]
4.问题
HttpInvoker服务综合了RMI服务和Hessian/Burlap服务的优点,但也存在一定限制:
- HttpInvoker服务仅是Spring框架所提供的远程服务调用方案,这就意味着客户端和服务端都必须是Spring应用。这暗含客户端和服务端都必须是基于java的。
- 由于使用的是java序列化机制,客户端和服务端必须使用相同的版本。
5.参考文档
1.Spring官方文档
2.《Spring实战(Spring IN ACTION)》