Hessian/Burlap是Caucho Technology提供的基于HTTP的轻量级的远程服务解决方案。其中Hessian同RMI一样,使用的是二进制消息进行客户端和服务端的交互。与其他的二进制进程调用技术(如RMI)不同的是,它的二进制消息可移植到其他非Java的语言中(如PHP/Python/C++/C#等)。
Hessian/Burlap是解决RMI限制的两套方案。
XML
的远程调用技术,因此能够移动到任何能解析XML
的语言上,相比Hessian的二进制格式而言,Burlap
可读性更强,此外和其他基于XML
的远程技术(如SOAP
或XML-PRC
)不同,Burlap
的消息结构尽可能的简单,不需要额外的外部定义语言(如WSDL
或IDL
)。Hessian
消息是二进制的,所以宽带上更具有优势。基于Hessian和Burlap的优缺点,如果我们比较注重可读性(如调试的目的)或者是应用需要与没有Hessian实现的语言交互,Burlap的xml消息是比较好的选择,由于Hessian的调用与Burlap相似,以下只针对Hessian进行。
即使不借助Spring框架,Hessian服务创建和调用也很简单。
——Hessian服务端
——Hessian客户端
——Common项目
注:客户端和服务端都需要导入Hessian的jar包依赖以及Common项目的jar依赖(实体和接口)。因此需要在项目pom.xml
添加如下jar包:
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.38version>
dependency>
<dependency>
<groupId>com.corpgroupId>
<artifactId>commonartifactId>
<version>1.0.0version>
dependency>
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);
}
AccountServiceImpl
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;
}
}
web.xml
配置 在web.xml
中将服务配置到Servlet引擎中,Servlet
配置主要是通过
和
来实现,前者声明的是Servlet对象,后者配置了Servlet映射。当访问
中的url映射路径时,便会路由到HessianServlet
引擎中,从而完成服务的调用。
<servlet>
<servlet-name>HessianServletservlet-name>
<servlet-class>com.caucho.hessian.server.HessianServletservlet-class>
<init-param>
<param-name>home-classparam-name>
<param-value>com.spring.service.AccountServiceImplparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>HessianServletservlet-name>
<url-pattern>/hessian_serviceurl-pattern>
servlet-mapping>
注:最后需要Hessian服务项目放到tomcat中,启动tomcat。
客户端的调用比较简单,添加Hessian的jar包依赖,可单元测试调用服务端。
public class TestHessian {
@Test
public void testHessian() throws MalformedURLException {
//localhost是ip,8080是端口,hessian_service是服务映射路径(见服务端)
String url = "http://localhost:8080/hessian_service/hessian";
HessianProxyFactory factory = new HessianProxyFactory();
factory.setOverloadEnabled(true);
AccountService accountService =(AccountService) factory.create(AccountService.class,url);
System.out.println(accountService.getAccounts("name"));
}
}
至此,Hessian的服务已经完成,可以看到即使不借助Spring框架,Hessian服务发布和客户端调用Hessian服务相当简单。
上节可以看到编写一个Hessian服务简单,Spring并不会简化Hessian的调用。但和Spring一起使用,Hessian服务可以各方面利用Spring框架的优势,这是纯Hessian所不具备的。包括Spring的AOP的可以为Hessian服务提供系统级服务,如声明式事务。为了演示Hessian服务调用,新建三个项目,具体介绍如下:
——common:定义了实体类和接口。
——HessianServer:服务端,实现common中的接口,提供远程服务。
——HessianConsumer:客户端,将远程服务RMI的代理注入,进行服务的调用。
重要的类:
DispatcherServlet/HttpRequestHandlerServlet:负责将符合配置URL的访问路径的请求转给URL控制器,最终被HessianServiceExporter处理。
HessianServiceExporter:HessianServiceExporter是一个Spring MVC控制器,可以接收Hessian请求,并将这些请求转换成对POJO的调用从而将POJO导出一个Hessian服务。
HessianProxyFactoryBean:HessionProxyFactoryBea/BurlapProxyFactoryBean生成代理对象通过HTTP(Hessian为二进制,Burlap为XML)与远程对象通信。
总结:通过以上分析可看到调用一个Hessian服务端的过程:Hessian客户端代理(HessianProxyFactoryBean)发起HTTP请求,服务端遇到该请求的HTTP的URL路径时,经DispatcherServlet或者HttpRequestHandlerServlet分发给HessianServiceExporter,HessianServiceExporter会请求转换成对服务实现类的调用,从而完成服务的调用。
步骤一:编写接口实现类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;
}
}
步骤二:配置一个Servlet
Hessian底层是使用HTTP进行通信的,HessianServiceExporter
实现为一个Spring MVC控制器。为此需要定义一个Servlet,有两种方式来定义Servlet,都需要在web.xml
进行配置。
DispatcherServlet
.<servlet>
<servlet-name>remotingservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>remotingservlet-name>
<url-pattern>/hessian/*url-pattern>
servlet-mapping>
HttpRequestHandlerServlet
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<servlet>
<servlet-name>accountExporterservlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>accountExporterservlet-name>
<url-pattern>/hessian/*url-pattern>
servlet-mapping>
步骤三:使用HessianServiceExporter
公开服务。
——XMl配置方式 :使用配置文件applicationContext.xml
如果采用的是DispatcherServlet
配置方式,那么在applicationContext.xml
中HessianServiceExporter
的bean名称
.
如果采用的是HessianServiceExporter
配置方式,
子标签
内容必须要与HessianServiceExporter的bean的名称要一致,即
。
那么服务完整的访问路径是
http://localhost:8080/remoting/accountExporter
<bean id="accountService" class="com.spring.service.AccountServiceImpl">bean>
<bean name="/accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter">
<property name="service" ref="accountService"/>
<property name="serviceInterface" value="com.spring.service.AccountService"/>
bean>
——Java类配置方式
@Configuration
public class HessianServerConfiguration {
@Bean
public HessianServiceExporter accountExporter(AccountService accountService) {
HessianServiceExporter hessianExporter = new HessianServiceExporter();
//service属性设置为实现接口AccountService类的bean引用.
hessianExporter.setService(accountService);
//serviceInterface属性设置为服务要实现的接口.
hessianExporter.setServiceInterface(AccountService.class);
//Hessian无注册表,无需为Hessian服务命名.
return hessianExporter;
}
@Bean
public HandlerMapping hessianMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
Properties mappings = new Properties();
//当访问路径是/hessian/accountExporter时,Servlet会请求转给accountExporter方法
mappings.setProperty("/accountExporter","accountExporter");
mapping.setMappings(mappings);
return mapping;
}
}
为了容器识别到java配置类,在applicationContext.xml
中开启注解:
<context:component-scan base-package="com.spring"/>
为了方便启动两个tomcat,客户端采用SpringBoot项目,在src/main/resources目录下新建文件application.properties
,修改tomcat端口。
server.port=8889
添加依赖pom.xml
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.0.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.corpgroupId>
<artifactId>commonartifactId>
<version>1.0.0version>
dependency>
<dependency>
<groupId>com.cauchogroupId>
<artifactId>hessianartifactId>
<version>4.0.38version>
dependency>
dependencies>
<properties>
<java.version>1.8java.version>
properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
步骤一:编写调用服务的类HessianController
.
@RestController
public class HessianController {
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;
}
}
步骤二:使用HessianProxyFactoryBean为远程服务代理
——XMl配置方式:使用配置文件applicationContext.xml
.
<bean class="com.spring.controller.HessianController">
<property name="accountService" ref="accountService"/>
bean>
<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/hessian_server/hessian/accountExporter"/>
<property name="serviceInterface" value="com.spring.service.AccountService"/>
bean>
——Java类配置方式
@Configuration
public class HessianClientConfiguration {
@Bean
public HessianProxyFactoryBean hessianProxy() {
HessianProxyFactoryBean hessianProxy = new HessianProxyFactoryBean();
hessianProxy.setServiceInterface(AccountService.class);
hessianProxy.setServiceUrl("http://localhost:8080/hessian_server/hessian/accountExporter");
return hessianProxy;
}
}
页面访问地址http://localhost:8889/home
完成客户端调用,也可以直接单元测试。
@Test
public void testRemoteHessian() throws MalformedURLException {
HessianProxyFactory hessianProxy = new HessianProxyFactory();
AccountService accountService= (AccountService)hessianProxy.
create(AccountService.class,"http://localhost:8080/hessian_server/hessian/accountExporter");
System.out.println(accountService.getAccounts("name"));
}
显示结果:
[{“name”:“DreamTech1113”}]
1.如果服务端为Hessian配置的Servlet是通过HttpRequestHandlerServlet
实现时:
ContextLoaderListener
,项目启动时不会报错,但是访问服务时,会报No WebApplicationContext found: no ContextLoaderListener registered?异常。ContextLoaderListener
,没有配置applicationContext.xml
文件会报
的名称必须要HessianServiceExporter
的bean名称要保持一致。2.Servlet是通过DispatcherServlet实现的,默认的是WEB-INF目录下的文件名称为"
名称-servlet.xml"的文件,可以通过
标签来指定。
3.Hessian/Burlap都是基于HTTP的,解决RMI防火墙问题,但当传递过来的RPC消息中包含序列化对象时,RMI完胜Hessian/Burlap。因为Hessian和Burlap采用的是私有的序列化机制,RMI使用的是Java本身的序列化机制,如果数据模型非常复杂,Hessian/Burlap的序列化模型可能无法胜任。
1.Spring官方文档
2.《Spring实战(Spring IN ACTION)》