動態代理的實際應用 前言 示例 實現 總結

原文鏈接

前言

最近在用 PythonSQLAlchemy 庫時(一個類似於 HibernateORM 框架),發現它的 Events 事件還挺好用。

簡單說就是當某張表的數據發生變化(曾、刪、改)時會有一個事件回調,這樣一些埋點之類的需求都可以實現在這裏,同時和業務代碼完全解耦,維護起來也很方便。

例如當訂單狀態發生變化需要發異步通知這樣的需求也可以利用這個實現。

根據我之前使用 Mybatis 的經驗,好像沒怎麼注意有這個功能,查閱了下發現 Hibernate 是支持的,只是我用得也少,所以也沒怎麼在意。

逐漸偏離主題。。。

說這些的主要原因是我打算爲之前寫的 cicada (輕量的 http 框架)加一個數據庫操作包,也實現類似的功能。

示例

最終的使用效果如下:

第一版本還比較粗糙,但功能都具備。

第一步:需要實現一個初始化接口,該接口會在應用初始化的時候執行。


緊接着我們需要定義一個 Model

@Data
@OriginName("user")
@ToString
public class User extends Model {
    @PrimaryId
    private Integer id ;
    private String name ;
    private String password ;

    @FieldName(value = "city_id")
    private Integer cityId ;

    private String description ;

}

它所對應的表結構如下:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `description` varchar(100) DEFAULT NULL,
  `roleId` int(11) DEFAULT NULL COMMENT '角色ID',
  `city_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

當需要查詢數據時:



便可以這樣訪問數據庫。


當需要更新數據時:



在初始化 DBHandle 時指定一個回調接口(也就是這裏的 UserUpdateListener),便可以在修改數據的時候拿到本次修改的數據實體。

@Slf4j
public class UserUpdateListener implements DataChangeListener {
    @Override
    public void listener(Object obj) {
        log.info("user update data={}", obj.toString());
    }
}

同時我們可以在控制檯看到數據修改時的回調結果:

這樣就實現了文初所提到的功能,便可以實現一些數據變化後需要執行的業務邏輯。

實現

下面重點來看看這個功能的實現過程;其實通過生成 DBHandle(數據庫增刪改的接口)實例的 API 便可以看出些端倪。

DBHandle handle = (DBHandle) new HandleProxy(DBHandle.class).getInstance(new UserSaveListener());

DBHandel 雖然是個接口,但是它並不是使用一個實現類來實現的,而是通過代理生成。

那通過代理生成比直接實例化實現類有啥好處呢?

舉個例子,比如現在你想買一個新手機。

第一種方式可以直接在官方旗艦店買一個標配的手機,沒有額外的東西只有一個手機。

當然你也可以在某些第三方經銷商那裏購買帶套餐的,比如套餐一在標配的基礎上多了保護殼、貼膜之類的附加屬性。

這個經銷商就類似於我們這裏的代理類,他可以在原有實現的基礎上新增一些東西,至於新增什麼全看你自己的需要了。

而之所以叫動態代理,也是因爲這個代理類是在程序運行過程中動態創建的,在編譯過程中並不能確定這個類的全限定名。


下面來看看這個代理類是如何生成的:


主要利用 JDK 自帶的 API 實現的,具體參數可以直接參考官方文檔:
https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html

總之這樣便可以創建一個 DBHandler 接口的代理對象,而真正的代理過程是在 InvocationHandler#invoke() 函數中實現的:

這裏的實現也是非常簡單,在實現完代理對象的業務邏輯後便回調我們傳入的事件接口,其中的參數便是當前的數據庫 Model 實體對象。

不過需要注意的是,這個事件回調和業務線程是同一個,所以寫在這裏的邏輯建議都爲異步(Hibernate 和 SQLAlchemy 都存在這個情況)。

總結

以上便是整個動態代理實現 ORM 監聽機制的全過程,其實可以看出並沒有它名稱那樣看起來高大上,當然本身實現也比較簡單。

同時也不止這一種實現方式,例如:

  • cglib
  • javassist
  • ASM

etc..

他們的具體實現及優劣就不在本文探討了,感興趣的後續我會將這個功能用這幾種方式實現一遍。

同時動態代理的應用也不止於此,比如:

  • RPC 中無感知的遠程調用。
  • Spring 中的 AOP、攔截器等。

後續會繼續完善這個 ORM 庫,甚至可以獨立出來作爲一個小巧的數據庫工具也未嘗不可。

相關源碼見此處:
https://github.com/TogetherOS/cicada

你的點贊與分享是對我最大的支持

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章