200行Java代碼如何實(shí)現(xiàn)依賴注入框架詳解
依賴注入介紹
先回顧下依賴注入的概念:
我們常提起的依賴注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個概念。具體含義是:當(dāng)某個角色(可能是一個Java實(shí)例,調(diào)用者)需要另一個角色(另一個Java實(shí)例,被調(diào)用者)的協(xié)助時,在 傳統(tǒng)的程序設(shè)計(jì)過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實(shí)例。但在Spring里,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此稱為控制反轉(zhuǎn);創(chuàng)建被調(diào)用者 實(shí)例的工作通常由Spring容器來完成,然后注入調(diào)用者,因此也稱為依賴注入。
其實(shí)簡單的說,依賴注入起到的作用就是講對象之間的依賴關(guān)系從原先的代碼中解耦出來,通過配置文件或注解等方式加上Spring框架的處理讓我們對依賴關(guān)系靈活集中的進(jìn)行管理。
依賴注入框架
依賴注入框架并不神秘,其實(shí)它是非常簡單的東西。不要去看spring的依賴注入源碼,因?yàn)槟阒灰蝗タ淳鸵馕吨阍僖矊懖桓蚁率肿约簲]了,它的功能因?yàn)檫^于強(qiáng)大,所以設(shè)計(jì)也過于復(fù)雜,普通程序員一眼看去只能望洋興嘆。
我也并沒有去細(xì)致閱讀spring源碼。即便如此也只用了半天的時間便自己擼了一個基本滿足標(biāo)準(zhǔn)依賴注入規(guī)范「JSR-330」的小框架iockids。這個小框架只有一個主類Injector,大約200行代碼,它具備以下功能。
- 單例/非單例注入
- 構(gòu)造器注入
- 字段注入
- 循環(huán)依賴注入
- Qualifier注入
我們看一個稍微復(fù)雜一點(diǎn)的使用示例
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import iockids.Injector;
@Singleton
class Root {
@Inject
@Named("a")
Node a;
@Inject
@Named("b")
Node b;
@Override
public String toString() {
return String.format("root(%s, %s)", a.name(), b.name());
}
}
interface Node {
String name();
}
@Singleton
@Named("a")
class NodeA implements Node {
@Inject
Leaf leaf;
@Inject
@Named("b")
Node b;
@Override
public String name() {
if (b == null)
return String.format("nodeA(%s)", leaf);
else
return String.format("nodeAWithB(%s)", leaf);
}
}
@Singleton
@Named("b")
class NodeB implements Node {
Leaf leaf;
@Inject
@Named("a")
Node a;
@Inject
public NodeB(Leaf leaf) {
this.leaf = leaf;
}
@Override
public String name() {
if (a == null)
return String.format("nodeB(%s)", leaf);
else
return String.format("nodeBWithA(%s)", leaf);
}
}
class Leaf {
@Inject
Root root;
int index;
static int sequence;
public Leaf() {
index = sequence++;
}
public String toString() {
if (root == null)
return "leaf" + index;
else
return "leafwithroot" + index;
}
}
public class Demo {
public static void main(String[] args) {
var injector = new Injector();
injector.registerQualifiedClass(Node.class, NodeA.class);
injector.registerQualifiedClass(Node.class, NodeB.class);
var root = injector.getInstance(Root.class);
System.out.println(root);
}
}
上面這份代碼用到了iockids提供的所有功能。
- Root/NodeA/NodeB類是單例類
- Leaf類是非單例類
- 它們都使用了字段注入
- NodeB使用了構(gòu)造器注入
- NodeA和NodeB還使用了Qualifier名稱注入
- Leaf類中有Root類型的字段,這便是循環(huán)依賴
- NodeA中有NodeB字段,NodeB中有NodeA字段,這也是循環(huán)依賴
為了便于理解上述代碼,我畫了依賴圖

上面的代碼輸出如下
root(nodeAWithB(leafwithroot0), nodeBWithA(leafwithroot1))
從這個輸出中,我們也可以大致想象出依賴結(jié)構(gòu)。
iockids提供了豐富的注入錯誤異常報(bào)告,防止用戶注入配置出錯。
比如我們將上面的NodeA和NodeB的名稱都配置成一樣的a,就會曝出下面的錯誤堆棧
iockids.InjectException: duplicated qualifier javax.inject.Named with the same class iockids.demo.Node at iockids.Injector.registerQualifiedClass(Injector.java:87) at iockids.Injector.registerQualifiedClass(Injector.java:70) at iockids.demo.Demo.main(Demo.java:106)
如果我們將NodeB的構(gòu)造器隨意加一個參數(shù)
@Inject
public NodeB(Leaf leaf, int k) {
this.leaf = leaf;
}
運(yùn)行時就會拋出下面的錯誤
iockids.InjectException: no accessible constructor for injection class int at iockids.Injector.createNew(Injector.java:120) at iockids.Injector.createNew(Injector.java:94) at iockids.Injector.createFromParameter(Injector.java:167) at iockids.Injector.createFromConstructor(Injector.java:145) at iockids.Injector.createNew(Injector.java:123) at iockids.Injector.createFromQualified(Injector.java:216) at iockids.Injector.createFromField(Injector.java:173) at iockids.Injector.injectMembers(Injector.java:233) at iockids.Injector.createNew(Injector.java:136) at iockids.Injector.createFromQualified(Injector.java:216) at iockids.Injector.createFromField(Injector.java:173) at iockids.Injector.injectMembers(Injector.java:233) at iockids.Injector.createNew(Injector.java:136) at iockids.Injector.createNew(Injector.java:94) at iockids.Injector.getInstance(Injector.java:245) at iockids.demo.Demo.main(Demo.java:107)
項(xiàng)目開源地址:https://github.com/pyloque/iockids (本地下載)
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
深入Spring Boot之ClassLoader的繼承關(guān)系和影響
這篇文章主要介紹了深入Spring Boot之ClassLoader的繼承關(guān)系和影響,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
解決@PathVariable出現(xiàn)點(diǎn)號.時導(dǎo)致路徑參數(shù)截?cái)喃@取不全的問題
這篇文章主要介紹了解決@PathVariable出現(xiàn)點(diǎn)號.時導(dǎo)致路徑參數(shù)截?cái)喃@取不全的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
淺談javaSE 面向?qū)ο?Object類toString)
下面小編就為大家?guī)硪黄獪\談javaSE 面向?qū)ο?Object類toString)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-06-06
java 算法之希爾排序詳解及實(shí)現(xiàn)代碼
這篇文章主要介紹了java 算法之希爾排序詳解及實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03
java字符串相加時的內(nèi)存表現(xiàn)和原理分析
這篇文章主要介紹了java字符串相加時的內(nèi)存表現(xiàn)和原理分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

