您的“關注”和“點贊”,是信任,是認可,是支持,是動力…
如意見相佐,可留言。
本人必將竭盡全力試圖做到準確和全面,終其一生進行修改補充更新。
文章目錄
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 的name
或id
值。添加內容,如下圖所示:
第四步:編寫測試類並使用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 進行實例化的哦。。