將 Hessian 集成到 Smart 中

Hessian,啥東西?
html

第一次見到這個單詞的時候,我真不知道它是什麼意思,雖然個人英語功底已經至關牛逼了。最後查了一下有道詞典才知道,原來 Hessian 就是「麻布袋子」啊!java

我真搞不懂,爲何一個麻布袋子就能經過 HTTP 發送二進制數據?可能麻布袋子不是通常的袋子,由於它密密麻麻,不是通常的數據能夠穿透它,除了二進制數據 0 和 1。git

若是您不想使用笨重的 SOAP WebService,也不想使用流行的 REST WebService,或許當您看到 Hessian 的功能後,它必定會讓您驚呆!web


通常咱們是這樣玩的,在服務端發佈 Hessian 服務,讓客戶端調用已發佈的 Hessian 服務。數據庫

請不要把 Hessian 想象得過於複雜與神祕,其實它不過是一個麻布袋子而已。緩存

在服務端咱們能夠這樣來發布 Hessian 服務:安全

@WebServlet("/hessian/user_service")
public class UserServiceImpl extends HessianServlet implements UserService {

    @Override
    public User login(String username, String password) {
        return DataSet.select(User.class, "username = ? and password = ?", username, password);
    }
}

咱們能夠將 Service 實現類發佈爲 Servlet(在類上使用 @WebServlet 註解,Servlet 3 規範,無需配置 web.xml 文件),而且讓它繼承 HessianServlet 類,那麼 Hessian 服務就發佈啦!架構

有沒有搞錯?真的這麼簡單?—— 沒有搞錯!果然就這麼簡單!框架

客戶端如何調用呢?ide

public class UserServiceHessianTest {

    @Test
    public void loginTest() throws Exception {
        String username = "admin";
        String password = "admin";

        String url = "http://localhost:8080/smart-sample/hessian/user_service";
        HessianProxyFactory factory = new HessianProxyFactory();
        UserService userService = (UserService) factory.create(UserService.class, url);

        User user = userService.login(username, password);
        Assert.assertNotNull(user);
    }
}

咱們使用 HessianProxyFactory 這個工廠類,藉助 UserService 接口與一個 HTTP 請求 URL,就能夠建立客戶端代理對象,經過這個代理對象來調用目標方法。

簡單而優雅!我只能這樣來形容 Hessian 了,並且它還安全且高效!由於咱們經過 HTTP 傳遞的其實是二進制數據,而並不是文本數據。

想進一步瞭解 Hessian 能夠閱讀一下它的官網:http://hessian.caucho.com/

官網還提供了一個入門指南,也不錯哦!http://hessian.caucho.com/doc/hessian-overview.xtp

當您打開 Hessian 官網,在您眼前必定會看到:

hessian binary web service protocol

The Hessian binary web service protocol makes web services usable without requiring a large framework, and without learning yet another alphabet soup of protocols. Because it is a binary protocol, it is well-suited to sending binary data without any need to extend the protocol with attachments.

翻譯一下大概是說:Hessian 是一種二進制 WebService 協議,它無需藉助一個牛逼框架來使用 WebService,也無需學習其它亂七八糟的協議。由於它是一種二進制協議,它很是適合於發送二進制數據,沒有任何須要來對現有協議進行擴展。

看來體育老師沒有白教我英語,我總算一口氣翻譯出來了。

它宣稱本身是 WebService,怪不得它敢說本身是跨平臺的,並且針對許多主流的開發語言都有相應的技術實現。

看來做爲一名開發人員,錯過了 Hessian 實屬不幸啊!但錯過了 Hessian 與 Smart 的集成,那就更爲不幸了。


衆所周知,Smart 的 Service 通常都是封裝業務邏輯的地方,包括複雜的計算與數據庫操做,能夠進行控制事務,也能夠進行數據緩存,還能夠進行方法攔截。這麼好的東西,若是帶上了 Servlet 這頂帽子,彷佛對於它真的有些虧!

爲了避免失 Smart Service 的種種特性,咱們須要進行一些巧妙的架構設計,就能夠解決這個問題。

怎麼作呢?

借鑑 DispatcherServlet 的思想,咱們也能夠搞一個 HessianDispatcherServlet 出來,讓它攔截全部的 Hessian 請求,根據 URL 來分發到指定的 Service 上。

提及來簡單,作起來如何呢?


第一步:建立 @Hessian 註解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Hessian {

    String value(); // Hessian URL
}

咱們建立了一個 @Hessian 註解,可將它標註在接口上,它只有一個 value 屬性,用於設置 Hessian URL。

帽子有了,帶在頭上看看效果如何呢?


第二步:配置須要發佈的 Hessian 服務

@Hessian("/user_service")
public interface UserService {

    User login(String username, String password);
}

咱們須要發佈哪一個 Service,就將這個 @Hessian 帽子戴在誰的頭上。

須要說明的是,若是戴在了某個 Service 接口頭上的話,那麼該接口的全部方法都會被髮布出來,其實這也正是咱們想作的事情。

須要注意的是,接口方法的參數或返回值都必須可被序列化,數據類型確定是能夠的,但對於 JavaBean 而言,咱們必須實現 Serializable 接口。咱們這裏的 User 是一個 Smart Entity,它必定是實現了 Serializable 接口的(因爲 User 繼承於 BaseEntity,它繼承於 BaseBean,它實現了 Serializable 接口)。


第三步:建立 HessianDispatcherServlet

@WebServlet(urlPatterns = HessianConstant.URL_PREFIX + "/*", loadOnStartup = 0)
public class HessianDispatcherServlet extends HessianServlet {

    // 定義一個 Hessian Servlet Map,用於管理 Hessian URL 與 Hessian Servlet 之間的映射關係
    private final Map<String, HessianServlet> hessianServletMap = new HashMap<String, HessianServlet>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        // 獲取全部標註了 @Hessian 註解的類(接口)
        List<Class<?>> hessianInterfaceList = ClassHelper.getClassListByAnnotation(Hessian.class);
        if (CollectionUtil.isNotEmpty(hessianInterfaceList)) {
            // 遍歷全部 Hessian 接口
            for (Class<?> hessianInterface : hessianInterfaceList) {
                // 獲取 Hessian URL
                String url = hessianInterface.getAnnotation(Hessian.class).value();
                // 獲取 Hessian 接口的實現類
                Class<?> implClass = IOCHelper.findImplementClass(hessianInterface);
                // 獲取實現類實例
                Object implInstance = BeanHelper.getBean(implClass);
                // 建立 Hessian Servlet
                HessianServlet hessianServlet = new HessianServlet();
                hessianServlet.setHomeAPI(hessianInterface); // 設置接口
                hessianServlet.setHome(implInstance); // 設置實現類實例
                hessianServlet.init(config); // 初始化 Servlet
                // 將 Hessian URL 與 Hessian Servlet 放入 Hessian Servlet Map 中
                hessianServletMap.put(HessianConstant.URL_PREFIX + url, hessianServlet);
            }
        }
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // 獲取請求 URL
        HttpServletRequest req = (HttpServletRequest) request;
        String url = WebUtil.getRequestPath(req);
        // 從 Hessian Servlet Map 中獲取 Hessian Servlet
        HessianServlet hessianServlet = hessianServletMap.get(url);
        if (hessianServlet != null) {
            // 執行 Servlet
            hessianServlet.service(request, response);
        }
    }
}

爲了讓 Hessian URL 與其它 URL 不一樣,咱們故意給它增長了一個前綴,在常量接口 HessianConstant 中提供了一個 URL_PREFIX 常量,代碼以下:

public interface HessianConstant {

    String URL_PREFIX = "/hessian";
}

其實 URL_PREFIX 叫什麼名字真的無所謂,關鍵是須要有別於其它普通 URL 才行,以避免被 Smart 的 DispatcherServlet 給截獲了。

經過閱讀 HessianDispatcherServlet 代碼及其相關注釋,不難理解:

  • 最核心的就是 Map<String, HessianServlet> hessianServletMap,有了它就能保證不一樣的 Hessian URL 能夠映射到不一樣的 Hessian Servlet 上。

  • 咱們經過遍歷全部帶有 @Hessian 註解的接口,來找到它們各自的實現類。經過建立 HessianServlet 實例,並設置 Home API(接口)與 Home(實現類實例),最後必定要調用 init 方法來初始化 Servlet。

  • 在 HessianDispatcherServlet 的 service 方法中,只是經過 URL 找到對應的 HessianServlet,並調用它的 service 方法來執行 Servlet。


經過以上三個步驟,就能夠實現 Smart Hessian 插件了,咱們只須要在 Maven 的 pom.xml 中這樣作便可使用該插件:

...
        <dependency>
            <groupId>com.smart</groupId>
            <artifactId>smart-plugin-hessian</artifactId>
            <version>${smart.version}</version>
        </dependency>
...

您能夠在 Smart Sample 中嘗試一下該功能,若是您打算將 Hessian 集成到您本身的框架中,相信本文會爲您提供一些幫助。


若是您也和我同樣有些潔癖,不喜歡看到太多的代碼細節,或許您會這樣提供一個 Hessian 客戶端 API:

public class HessianHelper {

    private static final Logger logger = LoggerFactory.getLogger(HessianHelper.class);

    @SuppressWarnings("unchecked")
    public static <T> T createClient(String hessianURL, Class<T> interfaceClass) {
        T client = null;
        try {
            HessianProxyFactory factory = new HessianProxyFactory();
            client = (T) factory.create(interfaceClass, hessianURL);
        } catch (MalformedURLException e) {
            logger.error("建立 Hessian 客戶端出錯!", e);
        }
        return client;
    }
}

有了 HessianHelper 咱們的 Hessian 客戶端編寫起來會更加輕鬆:

public class UserServiceHessianTest {

    @Test
    public void loginTest() throws Exception {
        String username = "admin";
        String password = "admin";

        String url = "http://localhost:8080/smart-sample/hessian/user_service";
        UserService userService = HessianHelper.createClient(url, UserService.class);

        User user = userService.login(username, password);
        Assert.assertNotNull(user);
    }
}

客戶端須要知道的就兩樣東西:接口與 URL。Hessian 這麻布袋子還不錯吧?


很是感謝 哈庫納 提供的技術指導!有了他的幫助,讓我少走了許多彎路。恰巧他今天也寫了一篇關於 Hessian 的文章,藉此向你們推薦一下:

http://my.oschina.net/u/1166271/blog/187509


相關代碼連接

Smart Hessian Plugin 代碼:http://git.oschina.net/huangyong/smart-plugin-hessian

Smart Framework 代碼:http://git.oschina.net/huangyong/smart-framework

Smart Sample 代碼:http://git.oschina.net/huangyong/smart-sample

相關文章
相關標籤/搜索