Spring远程服务----Hessian和Burlap实现远程调用

文章目录

        • 1.简介
        • 2.纯Hessian调用
          • 2.1 Common项目
          • 2.2 Hessian服务端
          • 2.3 Hessian客户端
        • 3.Spring整合Hessian
          • 3.1 介绍
          • 3.2 Common项目
          • 3.3 HessianServer
          • 3.4 HessianConsumer
            • 3.4.1 客户端调用
            • 3.4.2 单元测试
        • 4.问题
        • 5.参考

1.简介

Hessian/Burlap是Caucho Technology提供的基于HTTP的轻量级的远程服务解决方案。其中Hessian同RMI一样,使用的是二进制消息进行客户端和服务端的交互。与其他的二进制进程调用技术(如RMI)不同的是,它的二进制消息可移植到其他非Java的语言中(如PHP/Python/C++/C#等)。

Hessian/Burlap是解决RMI限制的两套方案。

  • Burlap:是一种基于XML的远程调用技术,因此能够移动到任何能解析XML的语言上,相比Hessian的二进制格式而言,Burlap可读性更强,此外和其他基于XML的远程技术(如SOAPXML-PRC)不同,Burlap的消息结构尽可能的简单,不需要额外的外部定义语言(如WSDLIDL)。
  • HessianHessian消息是二进制的,所以宽带上更具有优势。

  基于Hessian和Burlap的优缺点,如果我们比较注重可读性(如调试的目的)或者是应用需要与没有Hessian实现的语言交互,Burlap的xml消息是比较好的选择,由于Hessian的调用与Burlap相似,以下只针对Hessian进行。

2.纯Hessian调用

即使不借助Spring框架,Hessian服务创建和调用也很简单。
——Hessian服务端

  1. 服务端编写接口和实现类。
  2. 导入Hessian的jar包。
  3. 在Servlet中配置服务(在web.xml中配置服务)。

——Hessian客户端

  1. 客户端编写接口。
  2. 导入Hessian的jar包。
  3. 使用HessianProxyFactory来创建客户端代理来调用服务。

——Common项目

  • Hessian服务端和客户端都会使用接口和实体类,因此将实体类和接口定义到公共项目Common中,打成jar包,在客户端和服务端添加jar包依赖。

注:客户端和服务端都需要导入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>
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 Hessian服务端
  • 接口实现类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。

2.3 Hessian客户端

客户端的调用比较简单,添加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服务相当简单。

3.Spring整合Hessian

3.1 介绍

  上节可以看到编写一个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服务。
    Spring远程服务----Hessian和Burlap实现远程调用_第1张图片

  • HessianProxyFactoryBean:HessionProxyFactoryBea/BurlapProxyFactoryBean生成代理对象通过HTTP(Hessian为二进制,Burlap为XML)与远程对象通信。

Spring远程服务----Hessian和Burlap实现远程调用_第2张图片
总结:通过以上分析可看到调用一个Hessian服务端的过程:Hessian客户端代理(HessianProxyFactoryBean)发起HTTP请求,服务端遇到该请求的HTTP的URL路径时,经DispatcherServlet或者HttpRequestHandlerServlet分发给HessianServiceExporter,HessianServiceExporter会请求转换成对服务实现类的调用,从而完成服务的调用。

3.2 Common项目
  • common项目见上一节。
3.3 HessianServer

步骤一:编写接口实现类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.xmlHessianServiceExporter的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"/>
3.4 HessianConsumer

为了方便启动两个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>
3.4.1 客户端调用

步骤一:编写调用服务的类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完成客户端调用,也可以直接单元测试。

3.4.2 单元测试
@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”}]

4.问题

1.如果服务端为Hessian配置的Servlet是通过HttpRequestHandlerServlet实现时:

  • 如果在web.xml文件中没有配置监听器ContextLoaderListener,项目启动时不会报错,但是访问服务时,会报No WebApplicationContext found: no ContextLoaderListener registered?异常。
  • 配置了监听器ContextLoaderListener,没有配置applicationContext.xml文件会报
    parsing XML document from ServletContext resource [/]异常。
  • 的名称必须要HessianServiceExporter的bean名称要保持一致。

2.Servlet是通过DispatcherServlet实现的,默认的是WEB-INF目录下的文件名称为"名称-servlet.xml"的文件,可以通过标签来指定。
3.Hessian/Burlap都是基于HTTP的,解决RMI防火墙问题,但当传递过来的RPC消息中包含序列化对象时,RMI完胜Hessian/Burlap。因为Hessian和Burlap采用的是私有的序列化机制,RMI使用的是Java本身的序列化机制,如果数据模型非常复杂,Hessian/Burlap的序列化模型可能无法胜任。

5.参考

  1.Spring官方文档
  2.《Spring实战(Spring IN ACTION)》

你可能感兴趣的:(Spring框架)