Spring依賴注入三種方式(好的 壞的和丑的)

      網友投稿 1123 2022-05-30

      關于spring bean三種注入方式的優缺點對比,翻譯自Spring DI Patterns: The Good, The Bad, and The Ugly,水平有限,如有錯誤請指正。

      Spring開發者會很熟悉spring強大的依賴注入API,這些API可以讓你用@Bean的注解讓Spring實例化和管理Bean。Bean之間的任何依賴都會被spring解析和注入。

      三種依賴于注解的注入方法

      spring有三種注解的方式讓你來聲明類的依賴。

      字段注入(壞的)

      import org.springframework.beans.factory.annotation.Autowired; public class MyBean { @Autowired private AnotherBean anotherBean; //Business logic... }

      1

      2

      3

      4

      5

      6

      設值注入(丑的)

      import org.springframework.beans.factory.annotation.Autowired; public class MyBean { private AnotherBean anotherBean; @Autowired public void setAnotherBean(final AnotherBean anotherBean) { this.anotherBean = anotherBean; } //Business logic... }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      構造器注入(好的)

      public class MyBean { private final AnotherBean anotherBean; public MyBean(final AnotherBean anotherBean) { this.anotherBean = anotherBean; } //Business logic... }

      1

      2

      3

      4

      5

      6

      7

      字段注入難以忽視的真相

      這幾種方式中最常用的就是字段注入,很有可能是因為這是最方便的方式。不幸的是,因為它的普遍性,開發者很少了解到其他兩種方式相互之間的優缺點。

      使用字段注入的類會變得越來越難以維護

      當你用的字段注入模式,并且想在類里增加依賴時,你只需要加一個字段,然后加上@Autowired或者@Inject注解,然后就可以走了。聽起來很棒,但幾個月以后,你的類就會變成只有上帝才能理清楚的類了。 當然,這也很可能發生在另外兩中方式上,但是另兩種方式能迫使你更關注類中的依賴關系。

      只要你用了字段注入,單測就沒法做了

      當我看了Josh Long關于Spring boot的演講后,這句話就一直縈繞在我的腦海里, 從某種意義上來說,它也促使我寫下這篇文章。你怎么測試字段注入的類?很有可能你正在回想那些不太直觀的 Mockito 用法,就像這樣。

      import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class MyBeanTest { @Mock private AnotherBean anotherBean; @InjectMocks private MyBean target; //Tests... }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      這種利用反射的方式迫使開發者需要關注很多其他的地方,比如

      如果MyBean有多個其他依賴怎么辦?

      我是否應該創建一個target實例,或者只是聲明它?有什么不同?

      當依賴用到泛型的時候你是否能保證類型安全?

      如果你只需要部分依賴的真實實現怎么辦?

      用了字段注入的類都是非final的,容易產生循環依賴

      如果是你想把@Autowired自動注入的字段聲明為final類型的,編譯器會直接報錯,是不是很煩人。 而且這個字段只能被設置一次。除非你加了@Lazy注解,否則spring會在啟動的時候去解析依賴圖,你的bean可能因為循環依賴報出一個BeanCurrentlyInCreationException,例如:

      public class A { @Autowired private B b; } public class B { @Autowired private C c; } public class C { @Autowired private A a; }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      現實中肯定不會出現這么簡單的錯誤,但實際中可能會出現很多因為繼承、跨類庫,跨架構導致的依賴迷宮。這個問題可以提供把其中某個字段聲明為非必須(可以通過@Autowired(required = false)允許為空),或者使用懶加載(使用@Lazy可以再解析完bean之后再設值)。遇到過這個Exception的人都知道,找到循環依賴中確切的一環是非常耗時耗力的工作。一旦你找到了,你如何確定犧牲那個依賴呢?你怎么恰當的把這些寫到文檔里呢?

      spring中有很多種解決循環依賴的方法,而且現在有些方法開始變的很惡心了。

      最簡潔

      很多java開發者都喜歡這種方式

      便利會弱化代碼結構設計

      很難測試

      依賴不能是可變的(無法final)

      容易出現循環依賴

      需要使用到多個spring或者java注解

      設值注入

      模板和封裝

      三種方式里,設值注入是最模板化的,每個bean都必須有有個setter函數,每個setter函數必須加@Autowired或@Inject注解。這種方式你不用考慮你類依賴的數量問題,這算是另一種設計方式。 但你過多暴露類的內部,違反了開放封閉原則。

      設值注入讓單測變的簡單

      不需要反射的黑魔法,你只需要把你的依賴set進去。

      import org.junit.Before; import org.mockito.Mockito; public class MyBeanTest { private MyBean target = new MyBean(); private AnotherBean anotherBean = Mockito.mock(AnotherBean.class); @Before public void setUp() { myBean.setAnotherBean(anotherBean); } //Tests... }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      設值注入對循環依賴免疫

      使用設值注入,spring不會對你的bean做有向無環圖依賴分析,這就意味著可以有循環依賴。允許循環依賴是把雙刃劍,你不必處理那些因為循環依賴導致的惡心的問題,但你的代碼以后也就很難分解開了。 試試上BeanCurrentlyInCreationException只是在啟動時告訴你你的設計有問題。

      對循環依賴免疫

      隨著setter的添加,高度耦合的類很容易被識別出來。

      違反開放封閉原則

      會把循環依賴隱藏掉

      三種方法里最模板化的方式

      依賴不能是可變的(無法final)

      終結方案:構造器注入

      事實證明構造器注入是最佳的依賴注入解決方案。一些新的支持持續集成的平臺,比如Angular,已經從其他平臺吸取了教訓,只支持構造器注入。

      構造器注入能暴露出過度耦合的問題

      無論什么時候你的類需要一個新的依賴,你都得加一個構造參數,這就會強迫你去審視你類的耦合度。我發現少于3個依賴是比較好的,如果多于5個依賴,就應該重構了。只在短短幾行連續的代碼上數有多少個依賴是很容易的。

      額外的好處是,由于final字段可以在構造函數中初始化,所以我們的依賴關系可以是final的。恩,就應該是這樣!

      測試注入的構造函數類很簡單

      甚至比設值注入更簡單。

      Spring依賴注入的三種方式(好的 壞的和丑的)

      import org.mockito.Mockito; public class MyBeanTest { private AnotherBean anotherBean = Mockito.mock(AnotherBean.class); private MyBean target = new MyBean(anotherBean); //Tests... }

      1

      2

      3

      4

      5

      6

      注入子類的構造函數必須具有非默認構造函數

      使用構造函數注入的類的任何子類都必須具有調用父構造函數的構造函數。如果您繼承了Spring組件,這就很麻煩了。我個人很少碰到這種情況。我盡量避免在父組件中注入依賴——我通常是通過組合而不是繼承完成的。

      依賴可以是final的

      spring官方推薦的方式

      三種方式里最容易測試的方式

      高耦合類隨著構造參數的增長很容易被識別出來

      其他開發平臺的開發者也很熟悉

      不需要依賴@Autowired注解

      構造函數需要下沉到子類

      容易產生循環依賴

      結論

      有時候其他模式也有意義,但“為了與代碼庫的其余部分保持一致”和“使用字段注入模式更簡單”并不是有效的借口。

      例如,使用設值注入模式從xml setter注入方式遷移,或者需要修復BeanCurrentlyInCreationException問題時的中間狀態,但并不意味著你最終就應該是這樣。

      甚至字段注入模式也足夠了,例如,設計解決方案或回答StackOverflow上的問題時,除非他們的問題是關于Java中的依賴注入。在這種情況下,您應該用字段注入方便說明問題。

      Java Spring

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:好好編程-物流項目18【客戶管理-查詢客戶】
      下一篇:【技術方案分享】華為云OBS&RDS開發實戰-java版(中篇)
      相關文章
      亚洲AV成人一区二区三区AV| 亚洲AV无码成人精品区狼人影院| 亚洲午夜无码久久| 久久精品九九亚洲精品天堂| 久久亚洲AV成人无码国产电影| 久久精品国产99国产精品亚洲| 久久国产亚洲电影天堂| 日日噜噜噜噜夜夜爽亚洲精品| 亚洲AⅤ无码一区二区三区在线| 亚洲午夜精品久久久久久app| 91天堂素人精品系列全集亚洲| 亚洲色精品vr一区二区三区| 国产成人久久精品亚洲小说| 亚洲熟妇av一区二区三区下载| 亚洲韩国—中文字幕| 亚洲精品成人在线| 亚洲丰满熟女一区二区哦| 亚洲人成网站在线观看播放动漫 | 亚洲三级在线视频| 亚洲区小说区激情区图片区| 国产91精品一区二区麻豆亚洲| 成人伊人亚洲人综合网站222| 亚洲av午夜国产精品无码中文字| 一区二区亚洲精品精华液| 久久亚洲精品国产精品| 亚洲欧洲日产国码无码网站| 亚洲尤码不卡AV麻豆| 国产亚洲综合一区柠檬导航| 亚洲精品成人图区| 亚洲黄网在线观看| 亚洲午夜在线一区| 亚洲字幕AV一区二区三区四区| 亚洲AV日韩AV无码污污网站| 五月婷婷亚洲综合| 国产亚洲精久久久久久无码AV| 久久久亚洲欧洲日产国码农村| 97se亚洲综合在线| 亚洲色中文字幕在线播放| 亚洲成av人片在线观看天堂无码 | 亚洲不卡中文字幕| 亚洲av无码专区亚洲av不卡|