Spring 整合 Hessian 访问远程服务
目录
1.1 Hessian 简介
Hessian 是一个轻量级的 Web 服务实现工具,它采用的是二进制协议,因此很适合发送二进制数据。它的一个基本原理就是把远程服务对象以二进制的方式进行发送和接收。
1.2 整合
1.2.1 概述
对于 Hessian 而言,有服务端和客户端,所以我们的整合也需要分服务端的整合和客户端的整合。服务端的整合是通过 SpringMVC 进行的,而客户端的整合则是通过 Spring 的 bean 进行的。
1.2.2 服务端整合
基于客户端要调用服务端的远程服务,所以我们先来谈一下服务端的整合。 Hessian 的远程服务是基于接口的,所以我们要作为远程服务的实现类必须要实现一个接口。作为示例,这里我们建立一个叫 hessianServer 的 web 项目作为远程服务的服务端,在这个项目中我们建立一个叫做 UserService 的接口:
package com.tiantian.hessianserver.service; public interface UserService { public void addUser(); public void updateUser(); public void delUser(); public String findUser(String username); }
然后建立一个它的实现类 UserServiceImpl :
package com.tiantian.hessianserver.service.impl; import com.tiantian.hessianserver.service.UserService; public class UserServiceImpl implements UserService { public void addUser() { System.out.println("-------------invoke addUser()---------------"); } public void updateUser() { System.out.println("-------------invoke updateUser()---------------"); } public void delUser() { System.out.println("-------------invoke delUser()---------------"); } public String findUser(String username) { System.out.println("-------------invoke findUser---------------"); return "return: " + username; } }
那么接下来我们要做的就是把 UserServiceImpl 作为一个远程服务进行发布,以致客户端能够进行访问。
首先我们需要在 web.xml 中配置一个 SpringMVC 的 DispatcherServlet 用于接收所有的 Web 服务请求,这里我们这样配置:
<servlet> <servlet-name>hessianServer</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hessianServer</servlet-name> <url-pattern>/api/service/*</url-pattern> </servlet-mapping>
可以看到我们这个 DispatcherServlet 会处理 url 为“ /api/service/* ”的请求,通配符“ * ”就对应着我们的处理器映射。
接下来就是在 SpringMVC 的配置文件中利用 Hessian 来定义我们的远程服务了,这是通过 Spring 提供的 HessianServiceExporter 来实现的。我们需要在 SpringMVC 的配置文件中定义一个类型为 HessianServiceExporter 的 bean 对象。该 bean 对象需要接收两个属性,一个是 service 属性,用于关联真正的 service 对象,另一个是 serviceInterface 属性,用于指定当前的服务对应的接口。 HessianServiceExporter 实现了 HttpRequestHandler 接口,当我们请求某一个远程服务的时候实际上请求的就是其对应的 HessianServiceExporter 对象, HessianServiceExporter 会把请求的服务以二进制的方式返回给客户端。这里我们在 SpringMVC 的配置文件中这样定义:
<bean id="userService" class="com.tiantian.hessianserver.service.impl.UserServiceImpl" /> <bean name="/userService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="userService" /> <property name="serviceInterface" value="com.tiantian.hessianserver.service.UserService" /> </bean>
注意看我们的 HessianServiceExporter 对应的 bean 的 name 是“ /userService ”,在 SpringMVC 的配置文件中,当一个 bean 的 name 是以“ / ”开始的时候 Spring 会自动对它进行 BeanNameUrlHandlerMapping 。所以这里相当于是我们把“ /userService ”映射到了 HessianServiceExporter ,根据上面的配置我们要请求这个远程服务的时候应该请求“ /api/service/userService ”。虽然说在 Spring 的配置文件中我们把 bean 的名称设为以“ / ”开始时 Spring 会自动对它进行一个 beanName 映射,但有一次不知道是哪里影响了,我这样使用的时候 Spring 没有对它进行自动映射,所以为了保险起见,这里我们最好自己指定一个 BeanNameUrlHandlerMapping ,代码如下所示:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <bean id="userService" class="com.tiantian.hessianserver.service.impl.UserServiceImpl" /> <bean name="/userService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="userService" /> <property name="serviceInterface" value="com.tiantian.hessianserver.service.UserService" /> </bean>
注意,因为是根据 beanName 来进行映射的,所以我们必须要给 HessianServiceExporter bean 对象指定 name 属性,而且其对应的 name 必须以“ / ”开头,这样我们的客户端才能访问到对应的服务。
1.2.3 客户端整合
对于客户端要使用远程的 Hessian 服务的,我们需要在 Spring 的配置文件中定义对应的 org.springframework.remoting.caucho.HessianProxyFactoryBean bean 对象。 HessianProxyFactoryBean 对象需要指定两个属性,一个是 serviceInterface 属性,表示当前请求的远程服务对应的接口;另一个是 serviceUrl 属性,表示当前的远程服务对应的服务端请求地址。这里在客户端为了使用 hessianServer 定义的 UserService 服务,我们建立一个对应的 hessianClient 项目,在 hessianClient 中我们定义一个对应的 UserService 接口,这个接口的内容跟 hessianServer 中的 UserService 接口的内容是一样的。代码如下所示:
package com.tiantian.hessianserver.service; public interface UserService { public void addUser(); public void updateUser(); public void delUser(); public String findUser(String username); }
之后我们就在当前 Spring 的配置文件中定义对应的 HessianProxyFactoryBean 对象。 HessianProxyFactoryBean 会根据指定的 serviceInterface 和 serviceUrl 属性返回一个 serviceInterface 对应的代理对象。这里我们的 Spring 配置文件这样定义:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="userService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl" value="http://localhost:8080/hessianServer/api/service/userService" /> <property name="serviceInterface" value="com.tiantian.hessianserver.service.UserService" /> </bean> </beans>
可以看到我们通过 HessianProxyFactoryBean 定义了一个 UserService 对应的远程代理对象,之后我们就可以在我们的程序中把它作为一个普通的 bean 对象来使用这个 UserService 的代理对象了。这里我们定义以下测试代码:
package com.tiantian.hessianclient.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 com.tiantian.hessianserver.service.UserService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class HessianTest { @Autowired private UserService userService; @Test public void test() { userService.addUser(); userService.updateUser(); userService.delUser(); String user = userService.findUser("ZhangSan"); System.out.println(user); System.out.println("---------------------------------finished----------------------------------"); } }
之后我们启动我们的 hessianServer ,然后执行上面的测试程序,在服务端会输出如下内容:
在客户端会输出如下内容:
这说明我们已经成功地调用了远程服务 UserService 。
注:
1.Hessian不支持方法的重载,打个比方现在有一AddService,里面有一add(int a, int b)和一add(long a, long b)方法,然后我们把它发布为一个Hessian服务。那么当我们想要远程访问AddService的add方法时Hessian会报错,抛出异常
com.caucho.hessian.io.HessianProtocolException: '?' is an unknown code
因为默认情况下它是不支持方法的重载,这个时候我们可以在客户端使用的时候新增属性overloadEnabled,值为true。如:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="userService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl" value="http://localhost:8080/hessianServer/api/service/userService" /> <property name="serviceInterface" value="com.tiantian.hessianserver.service.UserService" /> <!--新增overloadEnabled属性,并把它的值设置为true,默认是false,则Hessian就能支持方法的重载了。--> <property name="overloadEnabled" value="true" /> </bean> </beans>
2.完整
源码请查看附件。