Spring 依賴注入(DI)詳解 [Spring][依賴注入的 6 種實現方式][setter注入][構造器注入][註解注入][自動裝配注入][靜態工廠注入][實例工廠注入]

您的“關注”和“點贊”,是信任,是認可,是支持,是動力…

如意見相佐,可留言。
本人必將竭盡全力試圖做到準確和全面,終其一生進行修改補充更新。

1 依賴注入概述

依賴注入,英文叫做 Dependency Injection,簡稱 DI

DI 和 IoC (《Spring IoC 容器詳解》)含義相同,它們是從兩個角度描述的同一個概念、做同一件事情。

當某個 Java 實例需要另一個 Java 實例時,使用 Spring 之前都是由調用者創建(使用 new 關鍵字獲得被調用者實例)被調用者的實例,而使用 Spring 框架後,被調用者的實例不再由調用者創建,而是由 Spring IoC 容器創建,這稱爲控制反轉,也即 IoC。

Spring IoC 容器在創建被調用者的實例時,會自動將調用者需要的對象實例注入給調用者,這樣,調用者通過 Spring IoC 容器獲得被調用者實例,這稱爲依賴注入,也即 DI。

Bean 的依賴注入方式,或叫 Bean 的裝配方式,有多種形式,比如接下來要介紹的基於 XML 的依賴注入(有兩種實現方式:Setter Injection 設置注入和 Constructor Injection 構造器注入)、基於 Annotation 的依賴注入、基於自動裝配、基於靜態工廠的方式的依賴注入和基於實例工廠的方式的依賴注入等。

2 依賴注入的多種方式(Bean 的裝配方式)

2.1 基於 XML 的依賴注入

2.1.1 Setter Injection(設置方法注入)

基於設置方法注入,也可以叫做setter方法注入,這是最簡單的注入方式。

指 IoC 容器使用 setter 方法注入被依賴的實例。通過調用無參構造器或無參 static 工廠方法實例化 bean 後,調用該 bean 的 setter 方法,即可實現基於 setter 的 DI。

案例實操,走你!

目的:在UserService接口的實現類UserServiceImpl中用setter方法初始化UserDao的對象。

具體步驟如下所示,

第一步:在com.manongajie.service包下創建UserService接口。如下圖所示:

在這裏插入圖片描述

第二步:在com.manongajie.serviceimpl包下創建接口UserService的實現類UserServiceImpl。在類中聲明userDao變量,但是沒有初始化它,這裏就必須要 setter 方法(是 IoC 容器的注入入口。相當於 IoC 容器調用 setter 方法初始化 userDao 變量)了,如下圖所示:

在這裏插入圖片描述

第三步:在 applicationContext.xml配置文件中添加配置信息。<property>標籤中的 name屬性值爲setXxx()方法的參數(也叫依賴項);ref屬性值爲 Bean 的nameid值。添加內容,如下圖所示:

在這裏插入圖片描述

第四步:編寫測試類並使用JUnit 測試運行。在com.manongajie.test包下的SpringTest類中創建test2()方法。如下圖所示:

在這裏插入圖片描述

從上圖中可以看出,使用 Spring IoC 容器獲取 userService的實例後,調用了該實例的 addUser()方法,在該方法中又調用了 UserDao 接口的實現類 UserDaoImpl 中的 save()方法(userDao 對象就是 IoC 容器自動調用 setter 方法注入的。本例中 setter 方法爲 setUserDao() 方法)。

2.1.2 Constructor Injection(構造器注入)

指 IoC 容器使用構造方法注入被依賴的實例。基於構造器的 DI 通過調用帶參數的構造方法(本文本小節以帶參數的構造器進行介紹)實現,每個參數代表一個依賴,Spring 容器會根據 bean 中指定的構造方法參數來決定調用哪個構造函數。

案例實操,走你!

目的:在UserDaoImpl類中創建成員變量userService,使用UserDaoImpl類的構造器注入userService 實例,也就是說 spring 容器在創建 UserDaoImpl類的實例時,也要將 userService實例作爲構造器的參數值傳到UserDaoImpl類中(配置文件中操作,使用<constructor-arg>標籤,ref屬性表示 Bean 的 id)。

具體步驟如下所示,

第一步:直接在之前的項目中操作,在UserDaoImpl類中創建一個成員變量userService,構造器UserService(UserService userService),如下圖所示:

在這裏插入圖片描述

第二步:配置配置文件,如下圖所示:

在這裏插入圖片描述

第三步:編寫測試類和JUnit 測試運行。如下圖所示:

在這裏插入圖片描述

從上圖中可以看出,通過 Spring 容器獲取UserDao的實例後,調用了該實例的save()方法,在該方法中又調用了UserService接口的實現類UserServiceImpl中的addUser()方法(userService 對象就是 spring 容器通過構造器注入的。)

2.2 基於 Annotation(註解)的依賴注入

2.2.1 基於 Annotation(註解)的依賴注入概述

如果應用中 Bean 的數量較多,基於 XML 配置文件實現 Bean 的裝配會導致 XML 配置文件過於臃腫,從而給維護和升級帶來一定的困難。

基於 Annotation(註解)的依賴注入就可以解決這個問題。

2.2.2 常用註解概述

  • @Component
    可以使用此註解描述 Spring 中的 Bean,但它是一個泛化的概念,僅僅表示一個組件(Bean),並且可以作用在任何層次。使用時只需將該註解標註在相應類上即可。

  • @Repository
    用於將數據訪問層(DAO層)的類標識爲 Spring 中的 Bean,其功能與 @Component 相同。

  • @Service
    通常作用在業務層(Service 層),用於將業務層的類標識爲 Spring 中的 Bean,其功能與 @Component 相同。

  • @Controller
    通常作用在控制層(如 Struts2 的 Action),用於將控制層的類標識爲 Spring 中的 Bean,其功能與 @Component 相同。

  • @Autowired(在本文 《2.3 基於自動裝配的依賴注入》 小節詳細介紹)
    用於對 Bean 的屬性變量、屬性的 Set 方法及構造函數進行標註,配合對應的註解處理器完成 Bean 的自動配置工作。默認按照 Bean 的類型進行裝配。

  • @Resource
    其作用與 Autowired 一樣。其區別在於 @Autowired 默認按照 Bean 類型裝配,而 @Resource 默認按照 Bean 實例名稱進行裝配。

    @Resource 中有兩個重要屬性:name 和 type。

    Spring 將 name 屬性解析爲 Bean 實例名稱,type 屬性解析爲 Bean 實例類型。
    如果指定 name 屬性,則按實例名稱進行裝配;
    如果指定 type 屬性,則按 Bean 類型進行裝配。
    如果都不指定,則先按 Bean 實例名稱裝配,如果不能匹配,則再按照 Bean 類型進行裝配;如果都無法匹配,則拋出 NoSuchBeanDefinitionException 異常。

  • @Qualifier
    與 @Autowired 註解配合使用,會將默認的按 Bean 類型裝配修改爲按 Bean 的實例名稱裝配,Bean 的實例名稱由 @Qualifier 註解的參數指定。

2.3.3 案例實操,走起

第一步:導入關鍵 JAR 包。然後創建 DAO 層接口,在com.manongajie.dao包下創建PersonDao接口,並添加add()方法。如下圖所示:

在這裏插入圖片描述
第二步:創建 DAO 層接口的實現類。在com.manongajie.daoimpl包下創建PersonDao接口的實現類PersonDaoImpl,並添加 DAO 層 add()方法。對類使用@Repository 註解將 PersonDaoImpl 類標識爲 Spring 中的 Bean,其寫法相當於配置文件中 <bean id="personDao" class="com.manongajie.daoimpl.PersonDaoImpl"/> 的書寫。如下圖所示:

在這裏插入圖片描述
第三步:創建 Service 層接口。圖下圖所示:

在這裏插入圖片描述
第四步:創建 Service 層接口的實現類。如下圖所示:

在這裏插入圖片描述

第五步:創建 Action 控制層。如下圖所示:

在這裏插入圖片描述
第六步:創建 Spring 配置文件。需要添加更多的約束文件,如何把約束文件交給 Eclipse 管理,請參見博文《開啓 Spring 之旅:第一個 Spring 程序 !》

在這裏插入圖片描述

第七步:創建測試類和 JUnit 運行測試。如下圖所示:

在這裏插入圖片描述

從上圖中可以看出,DAO 層、Service 層和 Action 層的 add()方法都成功輸出了結果。
So,使用 Annotation 裝配 Bean 的方式已經成功實現了。

2.3 基於自動裝配的依賴注入

本文篇幅太長了,另寫一文。

請參見博文《Spring 基於自動裝配的依賴注入詳解》

2.4 靜態工廠注入

需要提供一個靜態工廠方法 createBean(),創建 Bean 的實例(是指createBean()方法返回哪個類的實例,就創建哪個類的實例)。
需要在配置文件中,使用 <bean> 標籤的 factory-method 屬性,用於告訴 Spring 容器調用工廠類中的 createBean() 方法獲取 Bean 的實例。

案例演示,如下所示:

第一步:創建實體類 Person。在com.manongajie.static_factory包下創建。

在這裏插入圖片描述
第二步:創建靜態工廠類MyBeanFactory,並在類中創建createBean()靜態方法,用於創建 Bean 的實例。

在這裏插入圖片描述
第三步:創建配置文件。

在這裏插入圖片描述
第四步:創建測試類和JUnit 測試運行。

在這裏插入圖片描述
從以上運行結果可以看出,使用靜態工廠的方式也成功對 Bean 進行了實例化。

2.5 實例工廠注入

使用這種方式,工廠類不再使用靜態方法創建 Bean 的實例,而是直接在成員方法中創建 Bean 的實例。

也就是說需要一個成員方法createBean()來創建 Bean 的實例。

在配置文件中,需要實例化的 Bean 也不是通過 class 屬性直接指向其實例化的類,而是通過 factory-bean 屬性配置一個實例工廠,然後使用 factory-method 屬性確定使用工廠中的哪個方法。

案例演示,如下所示:

第一步:創建實體類 Person,並在類中添加say()方法。

在這裏插入圖片描述
第二步:創建實例工廠類,並添加成員方法createBean()

在這裏插入圖片描述
第三步:創建配置文件。

在這裏插入圖片描述
第四步:創建測試類和JUnit 測試運行。

在這裏插入圖片描述
從以上運行結果可以看出,使用實例工廠的方式也是可以對 Bean 進行實例化的哦。。

【文章其他地址】

微信公衆號:碼農阿杰

博客園

【參考資料】

spring

Artifact Repository Browser

Apache Commons

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章