前言
磨了許久,藉助最近的一次通宵上線 cicada 終於更新了 v2.0.0
版本。
之所以大的版本號變爲 2,確實是向下不兼容了;主要表現爲:
- 修復了幾個反饋的
bug
。 - 靈活的路由方式。
- 可拔插的
IOC
容器選擇。
其中重點是後面兩個。
新的路由方式
先來看第一個:路由方式的更新。
在之前的版本想要寫一個接口必須的實現一個 WorkAction
;而且最麻煩的是一個實現類只能做一個接口。
因此也有朋友給我提過這個 issue。
於是改進後的使用方式如下:
是否有點似曾相識的感覺😊。
如上圖所示,不需要實現某個特定的接口;只需要使用不同的註解即可。
同時也支持自定義 pojo
, cicada
會在調用過程中對參數進行實例化。
拿這個 getUser
接口爲例,當這樣請求時這些參數就會被封裝進 DemoReq
中.
http://127.0.0.1:5688/cicada-example/routeAction/getUser?id=1234&name=zhangsan
同時得到響應:
{"message":"hello =zhangsan"}
實現過程也挺簡單,大家查看源碼便會發現;這裏貼一點比較核心的步驟。
- 掃描所有使用
@CicadaAction
註解的類。 - 掃描所有使用
@CicadaRoute
註解的方法。 - 將他們的映射關係存入
Map
中。 - 請求時根據
URL
去Map
中查找這個關係。 - 反射構建參數及方法調用。
掃描類以及寫入映射關係
請求時查詢映射關係
反射調用這些方法
是否需要 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 會有一些自己的理解。
你的點贊與分享是對我最大的支持