一:前言
上一篇我们写了Spring如何配置RMI,但是RMI很难穿透防火墙,这一篇就让我们看看Spirng如何解决的?
Spring开发团队意识到RMI服务和基于HTTP的服务(例如Hessian和Burlap)之间的空白。一方面,RMI使用Java标准的对象序列化机制,但是很难穿透防火墙。另一方面,Hessian和Burlap能很好的穿透防火墙,但是使用私有的对象序列化机制。
就这样,Spring的Http invoker应运而生了。HttpInvoker是一个新的远程调用模型,作为Spring框架的一部分,能够执行基于Http的远程调用,并使用Java的序列化机制。使用基于HttpInvoker的服务和使用基于Hessian/Burlap的服务非常相似。
二:spring如何配置HttpInvoker
项目整体结构,和上一篇一样
第一步:配置实体类,接口和接口实现类,和上一篇文章一模一样,这里就不再赘述了,请参考上一篇文章
第二步:配置HttpInvokerServiceExpoter,将接口实现类JinNangServiceImpl导出为服务
package chapter15.spittr.config;
import java.util.Properties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import chapter15.spittr.service.JinNangServiceImpl;
import chapter15.spittr.service.interfaces.JinNangService;
@Configuration
@ComponentScan
public class RootConfig {
@Bean
public JinNangService jinNangService(){
return new JinNangServiceImpl();
}
@Bean
public HandlerMapping httpInvokerMapping(){
SimpleUrlHandlerMapping mapping=new SimpleUrlHandlerMapping();
Properties mappings=new Properties();
mappings.setProperty("/jinNang.service", "httpInvokerExporterJinNangService");
mapping.setMappings(mappings);
return mapping;
}
@Bean
public HttpInvokerServiceExporter httpInvokerExporterJinNangService(JinNangService jinNangService){
HttpInvokerServiceExporter rmiServiceExporter=new HttpInvokerServiceExporter();
rmiServiceExporter.setService(jinNangService);
rmiServiceExporter.setServiceInterface(JinNangService.class);
return rmiServiceExporter;
}
}
是否有点似曾相识,我们很难找出这个bean的定义和上一篇所声明的bean有什么不同,唯一的区别就是类名:HttpInvokerServiceExporter。否则的话,这个导出器和其他远程服务的导出器没有任何区别。
如下图所示,HttpInvokerServiceExporter的工作方式是:HttpInvokerServiceExporter也是一个Spring的MVC控制器,他通过DispatcherServlet接受来自客户端的请求,并将这些请求转换成实现服务的Pojo的方法调用。
因为HttpInvokerServiceExpoter是一个Spring MVC控制器,我们需要建立一个URL处理器,映射HTTP URL到对应的服务上,就像Hessian和Burlap导出器所作的一样:
@Bean
public HandlerMapping httpInvokerMapping(){
SimpleUrlHandlerMapping mapping=new SimpleUrlHandlerMapping();
Properties mappings=new Properties();
mappings.setProperty("/jinNang.service", "httpInvokerExporterJinNangService");
mapping.setMappings(mappings);
return mapping;
}
这样我们需要确保匹配了DispacherServlet,这样才能处理对“*.service”扩展的请求。
我们可以通过扩展
AbstractDispatcherServletInitializer或者 Abstract-AnnotationConfigDispatcherServletInitializer 的方式来配置DispatcherServlet,那么在重载getServletMappings()的时候,我们需要包含该映射:
package common;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import chapter15.spittr.config.RootConfig;
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class>[] { RootConfig.class };
}
@Override
protected Class>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" ,"*.service"};
}
}
到此我们已经创建好了服务器端。
三:配置客户端
使用Spring的HttpInvokerProxyFactoryBean,只需要在客户端的spring配置增加如下的@bean方法:
package chapter15.spittr.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;
import chapter15.spittr.service.interfaces.JinNangService;
@Configuration
@ComponentScan(basePackages={"chapter15.spittr"})
public class RootConfig {
@Bean
public HttpInvokerProxyFactoryBeanjinNangService(){
HttpInvokerProxyFactoryBeanrmiProxy=new HttpInvokerProxyFactoryBean();
rmiProxy.setServiceUrl("rmi://localhost/JinNangService");
rmiProxy.setServiceInterface(JinNangService.class);
return rmiProxy;
}
}
服务的URL是通过HttpInvokerProxyFactoryBean的serviceUrl属性设置的,在这里,服务名被设置为JinNangService,并且生命服务实在本地机器上的;同时,服务提供的接口由serviceInterface属性来指定。
现在我们已经把服务声明为Spring管理的bean,我们可以把它作为依赖装配进另一个bean中,就像任意非远程的bean那样。例如,我们可以使用JinNangService来获得一条锦囊妙计:
package chapter15.spittr.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import chapter15.spittr.config.RootConfig;
import chapter15.spittr.domain.JinNang;
import chapter15.spittr.service.interfaces.JinNangService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=RootConfig.class)
public class JaxwsTest {
@Autowired
JinNangService jinNangService;
@Test
public void getJinNang(){
JinNang jinnang=jinNangService.getJinNang();
System.out.println("获得的计策是:让"+jinnang.getPeople()+"实施"+jinnang.getJice());
}
}
客户端就会得到如下输出:
获得的计策是:让黄盖实施苦肉计
至此,服务端客户端代码已经全部写完了,怎么样,很简单吧!
总结:
Spring的HttpInvoker是作为两全齐美的远程调用解决方案而出现的,把Http的简单行和Java内置的对象序列化机制融合在一起,这使得HttpInvoker服务成为一个引人注目的替代RMI或者Hessian/Burlap的可选方案。
要记住HttpInvoker有一个重大的限制:他只是Spring框架所提供的远程调用解决方案。这意味着客户端和服务端都必须是Spring应用。并且,至少目前而言,也隐含表明客户端和服务端都必须是基于Java的。另外,因为使用了java的序列化机制,客户端和服务端必须使用相同版本的类(与RMI类似)。
以上全是作者纯手打,欢迎提出改进意见,谢谢!