这篇是承接《轻量级 Java 开发框架 设计》系列Blog文的后续文章,本文将介绍一下如何将 Hessian 整合进 Hasor。
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 的相关代码。