本身動手整合 Hessian 到 Hasor 中發佈服務

   Hessian 是一種通訊協議,使用 Hessian 能夠實現跨平臺的方法調用,有點相似 SOAP。您可使用下面任意一種語言經過 Hessian 協議進行互相調用,Hessian 自己使用的是二進制傳輸格式。 java

    好了廢話很少說,這篇文章是源於和 OSC 上的朋友進行討論最後得出的,如今拿出來和你們分享一下。 git

   首先咱們假想這樣一段代碼用來發布 Hessian 服務: github

@HessianService("/testBean")
public class HessianBean {
    public String sayHello() {
        System.out.println("aaa");
        return "aaa";
    }
}

而後使用下面這樣的代碼調用這個服務。 web

@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());
}

-------------------------------------------------------- api

    Hessian 發佈服務是使用一個 HessianServlet 進行的例以下面代碼:
HessianServlet hessianServlet = new HessianServlet();
hessianServlet.setAPIClass(HessianBean.class);
hessianServlet.setHome(hessianBean);

    在沒有容器支持的狀況下使用 Hessian 發佈服務須要寫一個 Servlet 類繼承自 HessianServle類而後設置 APIClass 和 Home 屬性。其中 APIClass 表示發佈的服務類型,Home 表示發佈的服務對象。 app

    當寫完擴展類以後將其配置到 web.xml 下,若是是 Servlet3.0 容器加上 @WebServlet  註解就能夠了。 maven

    下面看一看如何讓 Hasor 支持 Hessian,首先 Hasor 發佈 Servlet 是經過 WebApiBinder 接口。獲得這個接口有兩種方式。 測試

    第一種:經過 Hasor 的 WebPlugin 擴展機制,在loadPlugin 時候獲得它。
    第二種:經過 Hasor 的 WebModule 擴展機制,當 init 階段能夠獲得它。 網站

    如今不管你是選用了那種方式咱們假設您已經獲得了 WebApiBinder 接口。接下來就是經過上面這段代碼建立一個 Hessian Servlet 並註冊到 Hasor 中即可以了。代碼看上去應該是這個樣子的: ui

@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個註解一個插件就能夠搞定!

    代碼位於:

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 的相關代碼。

相關文章
相關標籤/搜索