動態代理的實際應用

原文連接html

前言

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

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

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

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

逐漸偏離主題。。。

說這些的主要緣由是我打算爲以前寫的 cicada (輕量的 http 框架)加一個數據庫操做包,也實現相似的功能。數據庫

示例

最終的使用效果以下:oracle

初版本還比較粗糙,但功能都具有。

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


緊接着咱們須要定義一個 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 ;

}

它所對應的表結構以下:ide

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

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

公衆號名片底部.jpg

相關文章
相關標籤/搜索