自己动手整合 Hessian 到 Hasor 中发布服务

本文详细介绍了使用Hessian协议进行跨平台方法调用的过程,包括服务发布、客户端调用、解析注解创建Hessian客户端等步骤。通过Hasor插件实现了简化服务发布流程,只需通过注解即可完成服务发布和调用,极大地提高了开发效率。

  Hessian 是一种通信协议,使用 Hessian 可以实现跨平台的方法调用,有点类似 SOAP。您可以使用下面任意一种语言通过 Hessian 协议进行互相调用,Hessian 本身使用的是二进制传输格式。

    好了废话不多说,这篇文章是源于和 OSC 上的朋友进行讨论最后得出的,现在拿出来和大家分享一下。

   首先我们假想这样一段代码用来发布 Hessian 服务:

?
1
2
3
4
5
6
7
@HessianService ( "/testBean" )
public class HessianBean {
     public String sayHello() {
         System.out.println( "aaa" );
         return "aaa" ;
     }
}

然后使用下面这样的代码调用这个服务。

?
1
2
3
4
5
6
7
8
9
@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());
}

--------------------------------------------------------

    Hessian 发布服务是使用一个 HessianServlet 进行的例如下面代码:
?
1
2
3
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 中便可以了。代码看上去应该是这个样子的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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 特性两者可以没有继承关系。下面是它们的代码:

?
1
2
3
4
5
6
7
8
9
10
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。

?
1
2
3
4
5
@Retention (RetentionPolicy.RUNTIME)
@Target ({ ElementType.TYPE })
public @interface HessianService {
     public String value();
}

    其次创建一个 Hasor Web Plugin 插件。

?
1
2
3
4
5
6
@Plugin
public class HessianPlugin extends AbstractWebHasorPlugin {
     public void loadPlugin(WebApiBinder apiBinder) {
         
     }
}

    接下来我们需要知道都有哪些类标记了 HessianServices 注解,这个工作可以通过 Hasor 提供的方法取得。

?
1
Set<Class<?>> servicesSet = apiBinder.getClassSet(HessianService. class );

    getClassSet 方法是由 Environment 接口直接提供,Hasor 在加载插件之前这个接口对象会被创建。该方法会自动扫描 classpath 路径中所有类,并匹配被扫描的类。扫描匹配规则如下:

    1.被扫描的类是否为被测试类的(子类)。
    2.被扫描的类中是否实现了被测试类型表示的接口(接口实现)。
    3.如果被测试的类型是注解,则判断被扫描的类中是否标记了该注解(注解)。

    通过上面这个方法就可以得到我们需要的那些类,接下来取得注解的信息注册 Hessian 服务。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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 的待遇。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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。

    下面是我们发布服务的最终完整插件代码,它只有一个类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@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));
                 }
             }
         });
     }
}

    下面代码就是测试程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//发布服务
@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());
}

--------------------------------------------------

    还记得前面说的

?
1
2
3
4
public static void main(String[] args) throws MalformedURLException {
   IHessianBean bean = (IHessianBean) HessianPlugin.getPropxy(IHessianBean. class );
   System.out.println(bean.sayHello());
}

    这种形式调用客户端么?下面是 getPropxy 的方法代码,将这个代码放入 HessianPlugin 类中即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建 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个注解一个插件就可以搞定!

    代码位于:

http://git.oschina.net/zycgit/hasor/tree/master/examples/src/main/java/net/test/project/common/hessian

    这里是 @黄勇 写的一篇基于 @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 的相关代码。

<p>Hasor开发框架是一款基于Java语言的应用程序开发框架,它的核心设计目标是提供一个简单、切必要的环境给开发者。开发者可以在此基础上,通过Hasor强有力的粘合机制,构建出更加完善的应用场景。同时Hasor的各种内置插件也会帮助开发者进行快速开发。 基于目前Hasor内核上已经发展出DB、Web、Restful三大独立的基础框架。</p><p>特征: 1、IoC/Aop编程模型,设计精巧,使用简单 2、COC原则的最佳实践,‘零’配置文件 3、微内核 + 扩展,基于内核已发展出DB、Web、Restful三大独立的基础框架 4、真正的零开发,解析项目特有的自定义Xml配置 5、支持模板化配置文件,程序打包之后一套配置通吃(日常、预发、线上)以及其它各种环境 6、完备的JDBC操作接口,支持Result -> Object映射 7、提供三种途径控制事务,支持七种事务传播属性,标准的事务隔离级别 8、支持多数据源、及多数据源下的事务控制(非JPA) 9、内置事件机制,方便进行业务深度解耦,使业务逻辑更佳清晰 10、支持Web类型项目开发,提供restful风格的mvc开发方式 11、支持Form表单验证、支持场景化验证 12、提供开放的模版渲染接口,支持各种类型的模版引擎 13、提供丰富的工具箱,帮助您快速开发,有了它您甚至不需要 apache-commons 14、支持log4j、logback等多种主流日志框架 15、体积小,无第三方依赖</p><p>最低要求: 1、jdk6 2、servlet 2.3</p>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值