依賴注入介紹
先回顧下依賴注入的概念:
我們常提起的依賴注入(dependency injection)和控制反轉(zhuǎn)(inversion of control)是同一個概念。具體含義是:當某個角色(可能是一個java實例,調(diào)用者)需要另一個角色(另一個java實例,被調(diào)用者)的協(xié)助時,在 傳統(tǒng)的程序設(shè)計過程中,通常由調(diào)用者來創(chuàng)建被調(diào)用者的實例。但在spring里,創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此稱為控制反轉(zhuǎn);創(chuàng)建被調(diào)用者 實例的工作通常由spring容器來完成,然后注入調(diào)用者,因此也稱為依賴注入。
其實簡單的說,依賴注入起到的作用就是講對象之間的依賴關(guān)系從原先的代碼中解耦出來,通過配置文件或注解等方式加上spring框架的處理讓我們對依賴關(guān)系靈活集中的進行管理。
依賴注入框架
依賴注入框架并不神秘,其實它是非常簡單的東西。不要去看spring的依賴注入源碼,因為你只要一去看就意味著你再也寫不敢下手自己擼了,它的功能因為過于強大,所以設(shè)計也過于復(fù)雜,普通程序員一眼看去只能望洋興嘆。
我也并沒有去細致閱讀spring源碼。即便如此也只用了半天的時間便自己擼了一個基本滿足標準依賴注入規(guī)范「jsr-330」的小框架iockids。這個小框架只有一個主類injector,大約200行代碼,它具備以下功能。
- 單例/非單例注入
- 構(gòu)造器注入
- 字段注入
- 循環(huán)依賴注入
- qualifier注入
我們看一個稍微復(fù)雜一點的使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
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)依賴
為了便于理解上述代碼,我畫了依賴圖
上面的代碼輸出如下
1
|
root(nodeawithb(leafwithroot0), nodebwitha(leafwithroot1)) |
從這個輸出中,我們也可以大致想象出依賴結(jié)構(gòu)。
iockids提供了豐富的注入錯誤異常報告,防止用戶注入配置出錯。
比如我們將上面的nodea和nodeb的名稱都配置成一樣的a,就會曝出下面的錯誤堆棧
1
2
3
4
|
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ù)
1
2
3
4
|
@inject public nodeb(leaf leaf, int k) { this .leaf = leaf; } |
運行時就會拋出下面的錯誤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 ) |
項目開源地址: https://github.com/pyloque/iockids
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://mp.weixin.qq.com/s/R14Xaq2iSUbVphdVtRiyjg