Java自動依賴注入框架:Pure.IoC
Pure.IoC
Pure.IoC是一個輕量級基于類和注解的自動依賴注入框架。
使用jdk 1.8
此框架依賴于Style函數式工具集v1.1.1
如果需要debug輸出,則還依賴于log4j 1.2.17
推薦與Spring配合使用
框架思想
以已有的邏輯關系代替復雜的配置。
設計思路
通常寫java時會使用Spring框架進行IoC管理。而Spring是基于bean的,配置起來首先需要將類映射到bean,然后描述bean的依賴關系。
事實上,很多時候依賴關系僅僅需要類型即可確定。比如說
class A{ ... public void setB(B b){ this.b=b; } } class B{ }
很顯然,A依賴于B,A需要將一個B的實例通過setB注入進來。這應當是很自然的邏輯關系。
類型依賴在編碼時就會設定好,所以,如果有工具能幫助完成這類“顯而易見”的依賴該多好。于是我開發了Pure.IoC
框架的框架
擴展性
設計Pure.IoC時經歷了不少思考。考慮下面這樣的“復雜”情況(其實已經算不復雜了)
@Singleton @Wire class Complex{ private ...... public Complex(){ ... } @Default public Complex(AnotherClass obj){ ... } public void setA(A a){ ... } public void setB(B b){ ... } public void setInterface(Interf1 interf){ ... } public void setInterface(@Use(clazz=Impl2.class)Interf2 interf){ ... } } @Default(clazz=Impl1.class) interface Interf1{ ... }
上述注解描述了:
- @Singleton Complex是一個單例
- @Wire 在通過AutoWire構建時需要注入
- @Default 指定默認的構造器(構造函數上),指定默認的實現類(接口上)
- @Use 指定使用的類
注解應當可以無限的附加,也不能增加系統復雜性。
所以,最終使用這種設計:
AnnotationHandler + HandlerChain
AnnotationHandler分為四種,
- SetterAnnotationHandler 負責調用Setter,考慮setter,對應成員,和setter參數上的注解
- ParamAnnotationHandler 負責根據類型獲取實例,考慮參數上的注解
- ConstructorFilter 負責選擇構造器,考慮構造器上的注解
- TypeAnnotationHandler 負責選取要構造的類型,考慮類型上的注解
Handler被注冊到IOCController上,根據注冊的順序進行handling操作。handler的內部實現有點像AOP,一般來說會調用chain.next().handle(...) ,然后根據返回值或者異常進行一些邏輯判斷。詳情見各Handler接口的handle方法文檔
所以,可以非常方便的進行注解的擴展。
循環依賴
循環依賴雖然不很常見,但沒準會遇到。A->B->C->A
從邏輯上,A,B,C中必須有一個是單例,否則對象的創建將無限循環下去。
另外: Spring要求構造器不能循環依賴,否則無法完成構建。
不過Pure.IoC巧妙的解決了循環依賴的問題。
- 所有注入在構造時進行。只有注入完成,構造才會完成。
- 對于單例,在構造實際進行前把自己的引用交給IOCController,指示單例已經存在
- 由于只有注入完成才構造完成,所以不必擔心獲取到一個構造一半的對象。
所以,即使是構造器中包含循環依賴,也能順利的進行(本質上所有依賴都是構造器依賴)。
適用性
Pure.IoC由于設計為構造時注入,所以不需要任何額外組建即可放入框架中使用。也可配合Spring使用。甚至可以一部分setter通過本框架注入,一部分通過Spring注入。
直接與Struts2等相接也可以。由于注入過程完全在類內部,所以并不需要像Spring一樣配置接管Struts2的對象工廠,而是直接就可使用(一個對象本身就是注入“工廠”)。
推薦的使用方法是
extends AutoWire
但是有些情況下必須繼承別的類,那么有兩種解決辦法:
1.
@Wire class A { ... }
@Wire注解標注的類在任何經過Pure.IoC的情況下都會進行注入操作。
在單獨獲取時可使用
AutoWire.get(A.class)
獲取實例
2.
class A { public A(){ AutoWire.wire(this); } }
構造時將自己的引用交給框架進行注入。
實際上所有入口最終都調用AutoWire.wire(Object)
如何使用?
首先使用上述三種方法任何一種進行框架接入。
默認行為
沒有任何注解參與時的默認行為是:
注入setter時,獲取參數類型并向IOCController請求實例,獲取實例后進行setter的invoke。
在獲取實例時,取得唯一一個構造器,或者取得無參數的構造器。
如果構造器有參數,則取得參數類型對應實例。
最后進行構造
擴展注解
Setter
-
Use(clazz, constant, variable)
clazz代表使用指定的類型作注入 接下來會對指定的類型進行Type檢查。
constant代表之前在IOCController上注冊的“常量”
variable代表之前在IOCController上注冊的“變量”,必須是一些static的成員或者方法。 -
Force(value)
將String類型的value轉化為對應的類型。Force只支持基本類型和String - Ignore
忽略該setter
Type
- Wire 指示該類需要注入
- IsSingleton
指示該類為單例 - Default(clazz)
一般用于interface或者abstract class
在Type檢查時若遇到該注解則會轉而構造該注解指定的類型
Constructor
- Default()
在可能產生歧義時指定構造時默認使用的構造函數
Param
- Use(clazz, constant, variable)
clazz代表使用指定的類型作注入 接下來會對指定的類型進行Type檢查。
constant代表之前在IOCController上注冊的“常量”
variable代表之前在IOCController上注冊的“變量”,必須是一些static的成員或者方法。 - Force(value)
將String類型的value轉化為對應的類型。Force只支持基本類型和String
此外,進行Param處理時還提供自動的基本類型注入。
若沒有找到可用的注入,則將檢查基本類型和數組類型,并初始化為默認初始值(0,false,array[length=0])