一、DI依賴注入
首先來介紹下Spring中有哪些注入方式?
我們先來思考
-
向一個類中傳遞資料的方式有幾種?
-
普通方法(set方法)
-
構造方法
-
-
依賴注入描述了在容器中建立bean與bean之間的依賴關係的過程,如果bean執行需要的是數字或字串呢?
-
引用型別
-
簡單型別(基本資料型別與String)
-
Spring就是基於上面這些知識點,為我們提供了兩種注入方式,分別是:
-
setter注入
-
簡單型別
-
引用型別
-
-
構造器注入
-
簡單型別
-
引用型別
-
依賴注入的方式已經介紹完,接下來挨個看下:
二、setter注入
-
對於setter方式注入引用型別的方式之前已經介紹過,簡單看下:
-
在bean中定義引用型別屬性,並提供可訪問的set方法
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
-
配置中使用property標籤ref屬性注入引用型別物件
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>
2.1 環境準備
環境準備:
-
建立一個Maven專案
-
pom.xml新增依賴
-
resources下新增spring的配置檔案
最終專案的結構如下:
(1)專案中新增BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl類
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
(3)編寫AppForDISet執行類,載入Spring的IOC容器,並從中獲取對應的bean物件
public class AppForDISet {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
2.2 注入引用資料型別
需求:在bookServiceImpl物件中注入userDao
1.在BookServiceImpl中宣告userDao屬性
2.為userDao屬性提供setter方法
3.在配置檔案中使用property標籤注入
步驟1:宣告屬性並提供setter方法
在BookServiceImpl中宣告userDao屬性,並提供setter方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
步驟2:配置檔案中進行注入配置
在applicationContext.xml配置檔案中使用property標籤注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
執行AppForDISet類,檢視結果,說明userDao已經成功注入。
2.3 注入簡單資料型別
需求:給BookDaoImpl注入一些簡單資料型別的資料
參考引用資料型別的注入,我們可以推出具體的步驟為:
1.在BookDaoImpl類中宣告對應的簡單資料型別的屬性
2.為這些屬性提供對應的setter方法
3.在applicationContext.xml中配置
思考:
引用型別使用的是<property name="" ref=""/>
,簡單資料型別還是使用ref麼?
ref是指向Spring的IOC容器中的另一個bean物件的,對於簡單資料型別,沒有對應的bean物件,該如何配置?
步驟1:宣告屬性並提供setter方法
在BookDaoImpl類中宣告對應的簡單資料型別的屬性,並提供對應的setter方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
步驟2:配置檔案中進行注入配置
在applicationContext.xml配置檔案中使用property標籤注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="databaseName" value="mysql"/>
<property name="connectionNum" value="10"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
說明:
value:後面跟的是簡單資料型別,對於引數型別,Spring在注入的時候會自動轉換,但是不能寫成
<property name="connectionNum" value="abc"/>
這樣的話,spring在將abc
轉換成int型別的時候就會報錯。
步驟3:執行程式
執行AppForDISet類,檢視結果,說明userDao已經成功注入。
注意:兩個property注入標籤的順序可以任意。
對於setter注入方式的基本使用就已經介紹完了,
-
對於引用資料型別使用的是
<property name="" ref=""/>
-
對於簡單資料型別使用的是
<property name="" value=""/>
三、構造器注入
3.1 環境準備
構造器注入也就是構造方法注入,還是先準備下環境:
-
建立一個Maven專案
-
pom.xml新增依賴
-
resources下新增spring的配置檔案
這些步驟和前面的都一致,大家可以快速的拷貝即可,最終專案的結構如下:
(1)專案中新增BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl類
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public void save() {
System.out.println("book dao save ...");
}
}
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
(2)resources下提供spring的配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
(3)編寫AppForDIConstructor執行類,載入Spring的IOC容器,並從中獲取對應的bean物件
public class AppForDIConstructor {
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
3.2 構造器注入引用資料型別
接下來,在上面這個環境中來完成構造器注入:
需求:將BookServiceImpl類中的bookDao修改成使用構造器的方式注入。
1.將bookDao的setter方法刪除掉
2.新增帶有bookDao引數的構造方法
3.在applicationContext.xml中配置
步驟1:刪除setter方法並提供構造方法
在BookServiceImpl類中將bookDao的setter方法刪除掉,並新增帶有bookDao引數的構造方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public BookServiceImpl(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
步驟2:配置檔案中進行配置構造方式注入
在applicationContext.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</beans>
說明:
標籤<constructor-arg>中
-
name屬性對應的值為建構函式中方法形參的引數名,必須要保持一致。
-
ref屬性指向的是spring的IOC容器中其他bean物件。
步驟3:執行程式
執行AppForDIConstructor類,檢視結果,說明bookDao已經成功注入。
3.3 構造器注入多個引用資料型別
需求:在BookServiceImpl使用建構函式注入多個引用資料型別,比如userDao
1.宣告userDao屬性
2.生成一個帶有bookDao和userDao引數的建構函式
3.在applicationContext.xml中配置注入
步驟1:提供多個屬性的建構函式
在BookServiceImpl宣告userDao並提供多個引數的建構函式
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao,UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}
步驟2:配置檔案中配置多引數注入
在applicationContext.xml中配置注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
說明:這兩個<contructor-arg>
的配置順序可以任意
步驟3:執行程式
執行AppForDIConstructor類,檢視結果,說明userDao已經成功注入。
3.4 構造器注入多個簡單資料型別
需求:在BookDaoImpl中,使用建構函式注入databaseName和connectionNum兩個引數。
參考引用資料型別的注入,我們可以推出具體的步驟為:
1.提供一個包含這兩個引數的構造方法
2.在applicationContext.xml中進行注入配置
步驟1:新增多個簡單屬性並提供構造方法
修改BookDaoImpl類,新增構造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}
步驟2:配置完成多個屬性構造器注入
在applicationContext.xml中進行注入配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="666"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>
</beans>
說明:這兩個<contructor-arg>
的配置順序可以任意
步驟3:執行程式
執行AppForDIConstructor類,檢視結果
上面已經完成了建構函式注入的基本使用,但是會存在一些問題:
-
當建構函式中方法的引數名發生變化後,配置檔案中的name屬性也需要跟著變,因為是形參的名字。
-
這兩塊存在緊耦合,具體該如何解決?
在解決這個問題之前,需要提前說明的是,這個引數名發生變化的情況並不多,所以上面的還是比較主流的配置方式,下面介紹的,大家都以瞭解為主。
方式一:刪除name屬性,新增type屬性,按照型別注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
-
這種方式可以解決建構函式形參名發生變化帶來的耦合問題
-
但是如果構造方法引數中有型別相同的引數,這種方式就不太好實現了
方式二:刪除type屬性,新增index屬性,按照索引下標註入,下標從0開始
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>
-
這種方式可以解決引數型別重複問題
-
但是如果構造方法引數順序發生變化後,這種方式又帶來了耦合問題
介紹完兩種引數的注入方式,具體我們該如何選擇呢?
-
強制依賴使用構造器進行,使用setter注入有概率不進行注入導致null物件出現
-
強制依賴指物件在建立的過程中必須要注入指定的引數
-
-
可選依賴使用setter注入進行,靈活性強
-
可選依賴指物件在建立過程中注入的引數可有可無
-
-
Spring框架倡導使用構造器,第三方框架內部大多數採用構造器注入的形式進行資料初始化,相對嚴謹
-
如果有必要可以兩者同時使用,使用構造器注入完成強制依賴的注入,使用setter注入完成可選依賴的注入
-
實際開發過程中還要根據實際情況分析,如果受控物件沒有提供setter方法就必須使用構造器注入
-
自己開發的模組推薦使用setter注入
四、總結
這裡主要講的是Spring的依賴注入的實現方式:
-
setter注入
-
簡單資料型別
<bean ...> <property name="" value=""/> </bean>
-
引用資料型別
<bean ...> <property name="" ref=""/> </bean>
-
-
構造器注入
-
簡單資料型別
<bean ...> <constructor-arg name="" index="" type="" value=""/> </bean>
-
引用資料型別
<bean ...> <constructor-arg name="" index="" type="" ref=""/> </bean>
-
-
依賴注入的方式選擇上
-
建議使用setter注入
-
第三方技術根據情況選擇
-