設計一個可拔插的 IOC 容器

前言

磨了許久,藉助最近的一次通宵上線 cicada 終於更新了 v2.0.0 版本。java

之因此大的版本號變爲 2,確實是向下不兼容了;主要表現爲:git

  • 修復了幾個反饋的 bug
  • 靈活的路由方式。
  • 可拔插的 IOC 容器選擇。

其中重點是後面兩個。github

新的路由方式

先來看第一個:路由方式的更新。數據庫

在以前的版本想要寫一個接口必須的實現一個 WorkAction;並且最麻煩的是一個實現類只能作一個接口。json

所以也有朋友給我提過這個 issue性能


因而改進後的使用方式以下:測試

是否有點似曾相識的感受😊。

如上圖所示,不須要實現某個特定的接口;只須要使用不一樣的註解便可。spa

同時也支持自定義 pojo, cicada 會在調用過程當中對參數進行實例化。prototype

拿這個 getUser 接口爲例,當這樣請求時這些參數就會被封裝進 DemoReq 中.3d

http://127.0.0.1:5688/cicada-example/routeAction/getUser?id=1234&name=zhangsan

同時獲得響應:

{"message":"hello =zhangsan"}

實現過程也挺簡單,你們查看源碼便會發現;這裏貼一點比較核心的步驟。

  • 掃描全部使用 @CicadaAction 註解的類。
  • 掃描全部使用 @CicadaRoute 註解的方法。
  • 將他們的映射關係存入 Map 中。
  • 請求時根據 URLMap 中查找這個關係。
  • 反射構建參數及方法調用。

掃描類以及寫入映射關係


請求時查詢映射關係


反射調用這些方法

是否須要 IOC 容器

上面那幾個步驟其實我都是一把梭寫完的,但當我寫到執行具體方法時感受有點意思了。

你們都知道反射調用方法有兩個重要的參數:

  • obj 方法執行的實例。
  • args.. 天然是方法的參數。

我第一次寫的時候是這樣的:

method.invoke(method.getDeclaringClass().newInstance(), object);

而後一測試,也沒問題。

當我寫完以後 review 代碼時發現不對:這樣這裏每次都會建立一個新的實例,並且反射調用 newInstance() 效率也不高。

這時我不自覺的想到了 Spring 中 IOC 容器,和這裏場景也很是的相似。

在應用初始化時將全部的接口實例化並保存到 bean 容器中,當須要使用時只須要從容器中獲取便可。

這樣只是會在啓動時作不少加載工做,但造福後代啊。

可拔插的 IOC 容器

因而我打算本身實現一個這樣的 bean 容器。

但在實現以前又想到一個 feature:

不如把實現 bean 容器的方案交給使用者選擇,能夠選擇使用 bean 容器,也能夠就用以前的每次都建立新的實例,就像 Spring 中的 prototype 做用域同樣。

甚至能夠自定義容器實現,好比將 bean 存放到數據庫、Redis 都行;固然通常人也不會這麼幹。

SPI 的機制也有點相似。

要實現上述的需求大體須要如下步驟:

  • 一個通用的接口,包含了註冊容器、從容器中獲取實例等方法。
  • BeanManager 類,由它來管理具體使用哪一種 IOC 容器。

因此首先定義了一個接口;CicadaBeanFactory:

包含了註冊和獲取實例的接口。

同時分別有兩個不一樣的容器實現方案。

默認實現;CicadaDefaultBean

也就是文中說道的,每次都會建立實例;因爲這種方式其實根本就沒有 bean 容器,因此也不存在註冊了。

接下來是真正的 IOC 容器;CicadaIoc

它將全部的實例都存放在一個 Map 中。

固然也少不了剛纔提到的 CicadaBeanManager,它會在應用啓動的時候將全部的實例註冊到 bean 容器中。

重點是圖中標紅的部分:

  • 須要根據用戶的選擇實例化 CicadaBeanFactory 接口。
  • 將全部的實例註冊到 CicadaBeanFactory 接口中。

同時也提供了一個獲取實例的方法:

就是直接調用 CicadaBeanFactory 接口的方法。


而後在上文提到的反射調用方法處就變爲:

bean 容器中獲取實例了;獲取的過程能夠是每次都建立一個新的對象,也能夠是直接從容器中獲取實例。這點對於這裏的調用者來講並不關心

因此這也實現了標題所說的:可拔插

爲了實現這個目的,我將 CicadaIoc 的實現單獨放到一個模塊中,以 jar 包的形式提供實現。

因此若是你想要使用 IOC 容器的方式獲取實例時只須要在你的應用中額外加入這個 jar 包便可。

<dependency>
    <groupId>top.crossoverjie.opensource</groupId>
    <artifactId>cicada-ioc</artifactId>
    <version>2.0.0</version>
</dependency>

若是不使用則是默認的 CicadaDefaultBean 實現,也就是每次都會建立對象。

這樣有個好處:

當你本身想實現一個 IOC 容器時;只須要實現 cicada 提供的 CicadaBeanFactory 接口,並在你的應用中只加入你的 jar 包便可。

其他全部的代碼都不須要改變,即可隨意切換不的容器。

固然我是推薦你們使用 IOC 容器的(其實就是單例),犧牲一點應用啓動時間帶來後續性能的提高是值得的。

總結

cicada 的大坑填的差很少了,後續也會作一些小功能的迭代。

尚未關注的朋友趕忙關注一波:

https://github.com/TogetherOS/cicada

PS:雖然沒有仔細分析 Spring IOC 的實現,但相信看完此篇的朋友應該對 Spring IOC 以及 SpringMVC 會有一些本身的理解。

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

相關文章
相關標籤/搜索