Hessian 是一种通信协议,使用 Hessian 可以实现跨平台的方法调用,有点类似 SOAP。您可以使用下面任意一种语言通过 Hessian 协议进行互相调用,Hessian 本身使用的是二进制传输格式。
好了废话不多说,这篇文章是源于和 OSC 上的朋友进行讨论最后得出的,现在拿出来和大家分享一下。
首先我们假想这样一段代码用来发布 Hessian 服务:
@HessianService("/testBean")
public class HessianBean {
public String sayHello() {
System.out.println("aaa");
return "aaa";
}
}
然后使用下面这样的代码调用这个服务。
@HessianClient("http://127.0.0.1:8082/testBean")
public interface IHessianBean {
public String sayHello();
}
public static void main(String[] args) throws MalformedURLException {
IHessianBean bean = (IHessianBean) HessianPlugin.getPropxy(IHessianBean.class);
System.out.println(bean.sayHello());
}
--------------------------------------------------------
HessianServlet hessianServlet = new HessianServlet();
hessianServlet.setAPIClass(HessianBean.class);
hessianServlet.setHome(hessianBean);
在没有容器支持的情况下使用 Hessian 发布服务需要写一个 Servlet 类继承自 HessianServle类然后设置 APIClass 和 Home 属性。其中 APIClass 表示发布的服务类型,Home 表示发布的服务对象。
当写完扩展类之后将其配置到 web.xml 下,如果是 Servlet3.0 容器加上 @WebServlet 注解就可以了。
下面看一看如何让 Hasor 支持 Hessian,首先 Hasor 发布 Servlet 是通过 WebApiBinder 接口。得到这个接口有两种方式。
第一种:通过 Hasor 的 WebPlugin 扩展机制,在loadPlugin 时候得到它。
第二种:通过 Hasor 的 WebModule 扩展机制,当 init 阶段可以得到它。
现在无论你是选用了那种方式我们假设您已经得到了 WebApiBinder 接口。接下来就是通过上面这段代码创建一个 Hessian Servlet 并注册到 Hasor 中便可以了。代码看上去应该是这个样子的:
@Plugin
public class TestHessian extends AbstractWebHasorPlugin {
public void loadPlugin(WebApiBinder apiBinder) {
HessianBean hessianBean = new HessianBean();
//
HessianServlet hessianServlet = new HessianServlet();
hessianServlet.setAPIClass(HessianBean.class);
hessianServlet.setHome(hessianBean);
//
apiBinder.serve("/hessian/test").with(hessianServlet);
}
public static void main(String[] args) throws MalformedURLException {
String url = "http://127.0.0.1:8082/hessian/test";
HessianProxyFactory factory = new HessianProxyFactory();
// IHello为调用的服务接口,url为hessian服务url
IHessianBean bean = (IHessianBean) factory.create(IHessianBean.class, url);
System.out.println(bean.sayHello());
}
}
其中 Main 方法是用于测试 这个服务发布是否成功。
HessianBean 和 IHessianBean 之间一个是用于发布服务的服务对象,另一个是服务抽象接口。根据 Java Hessian Api 特性两者可以没有继承关系。下面是它们的代码:
public class HessianBean {
public String sayHello() {
System.out.println("aaa");
return "aaa";
}
}
public interface IHessianBean {
public String sayHello();
}
启动 web 程序,并运行 main 方法可以看到控制台上打印出 aaa,客户端也打印出一份 aaa。
--------------------------------------------------------
或许各位同学觉得,这样写太死了不能满足我们那么灵活的要求。倘若我需要发布其它 Services 岂不要重复写很多代码,这样写也不是很优雅!
要想实现上面这样的目标我们需要自己解析 @HessianService 注解,然后发布 HessianServlet 服务。接下来我们开发一个 HessianPlugin 类用于创建 Hessian客户端对象。
我们知道通过 Java 反射机制可以轻松获取标记到 Class 上的注解信息,然后运用这些信息去做发布服务和调用服务。值得庆幸的是服务发布和调用代码在上面已经先给出了。那么接下来就是反射形式取得注解信息然后发布服务了。
首先定义一个 @HessianService 注解,所有标记了这个注解的类都被列入 Hessian Bean。
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface HessianService {
public String value();
}
其次创建一个 Hasor Web Plugin 插件。
@Plugin
public class HessianPlugin extends AbstractWebHasorPlugin {
public void loadPlugin(WebApiBinder apiBinder) {
}
}
接下来我们需要知道都有哪些类标记了 HessianServices 注解,这个工作可以通过 Hasor 提供的方法取得。
Set<Class<?>> servicesSet = apiBinder.getClassSet(HessianService.class);
getClassSet 方法是由 Environment 接口直接提供,Hasor 在加载插件之前这个接口对象会被创建。该方法会自动扫描 classpath 路径中所有类,并匹配被扫描的类。扫描匹配规则如下:
1.被扫描的类是否为被测试类的(子类)。
2.被扫描的类中是否实现了被测试类型表示的接口(接口实现)。
3.如果被测试的类型是注解,则判断被扫描的类中是否标记了该注解(注解)。
通过上面这个方法就可以得到我们需要的那些类,接下来取得注解的信息注册 Hessian 服务。
@Plugin
public class HessianPlugin extends AbstractWebHasorPlugin {
public void loadPlugin(WebApiBinder apiBinder) {
Set<Class<?>> servicesSet = apiBinder.getClassSet(HessianService.class);
for (Class<?> hessian : servicesSet) {
//
HessianService hessAnno = hessian.getAnnotation(HessianService.class);
if (hessAnno == null || StringUtils.isBlank(hessAnno.value()))
continue;
String pushPath = hessAnno.value();
pushPath = (pushPath.charAt(0) != '/') ? ("/" + pushPath) : pushPath;
//
HessianServlet hessianServlet = new HessianServlet();
hessianServlet.setAPIClass(hessian);
hessianServlet.setHome(hessian.newInstance());
apiBinder.serve(pushPath).with(hessianServlet);
//
}
}
}
OK,到此为止发布服务的工作就算完成了。或许“hessian.newInstance()”这么草草的通过反射创建 Services 会让服务类丢失 Guice IoC/Aop 强大功能的支持,但是我们的确完成了发布工作。
如果想要发布的服务类对象是通过 Guice 创建而来的自然就更加完美了,这样 Hessian 服务类也可以享受到 IoC/Aop 的待遇。
@Plugin
public class HessianPlugin extends AbstractWebHasorPlugin {
public void loadPlugin(WebApiBinder apiBinder) {
Set<Class<?>> servicesSet = apiBinder.getClassSet(HessianService.class);
// 1.注册
Map<Class<?>, HessianServlet> serviceMap = new HashMap<Class<?>, HessianServlet>();
for (Class<?> serviceType : servicesSet) {
//
HessianService hessAnno = serviceType.getAnnotation(HessianService.class);
if (hessAnno == null || StringUtils.isBlank(hessAnno.value()))
continue;
String pushPath = hessAnno.value();
pushPath = (pushPath.charAt(0) != '/') ? ("/" + pushPath) : pushPath;
//
HessianServlet serviceServlet = new HessianServlet();
//serviceServlet.setAPIClass(hessian);
//serviceServlet.setHome(hessian.newInstance());
serviceMap.put(serviceType, serviceServlet);
apiBinder.serve(pushPath).with(serviceServlet);
//
}
// 2.初始化
}
}
新的插件类改为如上样子,我们通过 serviceMap 保存 Hessian 服务和具体 HessianServlet 之间的映射关系。
通过 AppContextAware 接口取得 AppContext 对象然后再设置 HessianServlet的 APIClass 和 Home 属性即可 。
PS:Hasor 会在 Start 的第一时间通知 AppContextAware 接口并将 AppContext 输送进来,此时 AppContext 已经准备好可以通过它创建或者获取 Bean,其它模块还尚未 Start。
下面是我们发布服务的最终完整插件代码,它只有一个类:
@Plugin
public class HessianPlugin extends AbstractWebHasorPlugin {
public void loadPlugin(WebApiBinder apiBinder) {
Set<Class<?>> servicesSet = apiBinder.getClassSet(HessianService.class);
// 1.注册
final Map<Class<?>, HessianServlet> serviceMap = new HashMap<Class<?>, HessianServlet>();
for (Class<?> serviceType : servicesSet) {
//
HessianService hessAnno = serviceType.getAnnotation(HessianService.class);
if (hessAnno == null || StringUtils.isBlank(hessAnno.value()))
continue;
String pushPath = hessAnno.value();
pushPath = (pushPath.charAt(0) != '/') ? ("/" + pushPath) : pushPath;
//
HessianServlet serviceServlet = new HessianServlet();
serviceMap.put(serviceType, serviceServlet);
apiBinder.serve(pushPath).with(serviceServlet);
}
// 2.初始化
apiBinder.registerAware(new AppContextAware() {
public void setAppContext(AppContext appContext) {
for (Entry<Class<?>, HessianServlet> ent : serviceMap.entrySet()) {
Class<?> serviceType = ent.getKey();
HessianServlet serviceServlet = ent.getValue();
serviceServlet.setAPIClass(serviceType);
// 通过 AppContext 创建
serviceServlet.setHome(appContext.getInstance(serviceType));
}
}
});
}
}
下面代码就是测试程序:
//发布服务
@HessianService("/testBean")
public class HessianBean {
@Inject
private AppContext appContext;
//
public long sayHello() {
long t = appContext.getStartTime();
System.out.println(t);
return t;
}
}
//定义客户端接口
public interface IHessianBean {
public long sayHello();
}
//客户端调用
public static void main(String[] args) throws MalformedURLException {
String url = "http://127.0.0.1:8082/testBean";
HessianProxyFactory factory = new HessianProxyFactory();
// IHello为调用的服务接口,url为hessian服务url
IHessianBean bean = (IHessianBean) factory.create(IHessianBean.class, url);
System.out.println(bean.sayHello());
}
--------------------------------------------------
还记得前面说的
public static void main(String[] args) throws MalformedURLException {
IHessianBean bean = (IHessianBean) HessianPlugin.getPropxy(IHessianBean.class);
System.out.println(bean.sayHello());
}
这种形式调用客户端么?下面是 getPropxy 的方法代码,将这个代码放入 HessianPlugin 类中即可:
// 创建 Hessian 客户端调用
public static <T> T getPropxy(Class<T> propxyFaces) throws MalformedURLException {
HessianClient hessAnno = propxyFaces.getAnnotation(HessianClient.class);
if (hessAnno == null || StringUtils.isBlank(hessAnno.value()))
return null;
return getPropxy(propxyFaces, hessAnno.value());
}
// 创建 Hessian 客户端调用
public static <T> T getPropxy(Class<T> propxyFaces, String url) throws MalformedURLException {
if (propxyFaces.isInterface() == false)
throw new ClassCastException("propxyFaces is not Interface.");
HessianProxyFactory factory = new HessianProxyFactory();
return (T) factory.create(propxyFaces, url);
}
--------------------------------------------------
整合 Hessian 就这么多,如果您有更好的想法可以在此基础上扩展,通过 Hasor 整合 Hessian 就是这么简单。2个注解一个插件就可以搞定!
代码位于:
这里是 @黄勇 写的一篇基于 @WebServlet 注解下集成 Hessian 与大家分享一下。
http://my.oschina.net/huangyong/blog/187561
--------------------------------------------------
目前的开发代码存放于(包括Demo程序):
Github: https://github.com/zycgit/hasor
git@OSC: http://git.oschina.net/zycgit/hasor
非常感谢您百忙之中抽出时间来看这一系博文。可以通过Maven 中央仓库网站 http://search.maven.org/ 搜索 Hasor 下载 hasor 的相关代码。
来源:oschina
链接:https://my.oschina.net/u/1166271/blog/187509