1.依賴注入
(1) 依賴注入(DI)的概念:某個bean的依賴項,由容器來負責注入維護,而非我們自己手動去維護,以此來達到bean之間解耦的目的,如下
//情況一:不使用依賴注入
public class A {}
public class B {
//B依賴了A物件,這種依賴關系是由我們自己手動來維護的,編碼于代碼之中,是強依賴
private A a = new A();
}
//情況二:使用依賴注入
@Component
public class A {}
@Component
public class B {
//B依賴了A物件,這個A物件是由容器來提供的,無需我們關心
@Autowired
private A a;
}
(2) 依賴注入的兩種方式
- 基于建構式的依賴注入:容器通過呼叫帶有引數的建構式來完成依賴項的注入,其中建構式中的每個引數都代表一個依賴項,其引數決議規則如下
//例一:
//ExampleA繼承自ExampleB
public class ExampleA extends ExampleB {}
public class ExampleB {}
public class Combine {
//Combine依賴了ExampleA和ExampleB
public Combine(ExampleA a, ExampleB b) {
}
}
<!-- xml組態檔 -->
<beans ...>
<bean id="exampleA" ></bean>
<bean id="exampleB" ></bean>
<bean id="combine" >
<!-- Spring會按照型別進行精確匹配,因此1會被注入到建構式的第二個引數b中,而2會被注入到建構式的第一個引數a中,此時與這些建構式標簽宣告的先后順序無關 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>
</beans>
//例二:
//將Combine建構式變更一下
public class Combine {
//建構式引數均為ExampleB
public Combine(ExampleB b1, ExampleB b2) {
}
}
<!-- xml組態檔 -->
<bean id="combine" >
<!-- 此時無法進行精確匹配,因為建構式引數均為ExampleB,這時就會按照這些建構式標簽宣告的先后順序進行依賴項的注入,
結果為1會被注入到建構式的第一個引數b1中,而2會被注入到建構式的第二個引數b2中,如果將這兩個建構式標簽的宣告順序顛倒一下,結果也會隨之相反 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>
//例三
public class ExampleC {
public ExampleC(int number, String str) {
}
}
<!-- xml組態檔1 -->
<beans ...>
<bean id="c" >
<!-- 此時的Spring無法判斷2000或50000究竟是一個int還是一個String,因此它首先會采取例二中的辦法,按建構式標簽的宣告順序進行注入,結果為number被注入2000,str被注入字串50000 -->
<constructor-arg value="https://www.cnblogs.com/shame11/p/2000"></constructor-arg>
<constructor-arg value="https://www.cnblogs.com/shame11/p/50000"></constructor-arg>
</bean>
</beans>
<!-- xml組態檔2 -->
<beans ...>
<bean id="c" >
<!-- 可以使用type屬性,指定希望注入的引數的型別,來解決歧義問題,結果number被注入50000,str被注入字串2000,但是如果建構式引數串列中有多個型別相同的引數,則又會按照建構式標簽的宣告順序進行注入 -->
<constructor-arg type="java.lang.String" value="https://www.cnblogs.com/shame11/p/2000"></constructor-arg>
<constructor-arg type="int" value="https://www.cnblogs.com/shame11/p/50000"></constructor-arg>
</bean>
</beans>
<!-- xml組態檔3 -->
<bean id="c" >
<!-- 使用index屬性,按建構式引數索引位置進行注入,index屬性為0的建構式標簽的值會被注入到建構式的第一個引數,以此類推 -->
<constructor-arg index="1" value="https://www.cnblogs.com/shame11/p/50000"></constructor-arg>
<constructor-arg index="0" value="https://www.cnblogs.com/shame11/p/2000"></constructor-arg>
</bean>
<!-- xml組態檔4 -->
<bean id="c" >
<!-- 使用name屬性,按照建構式引數名稱進行注入,不過要注意,Spring要求使用這種方式時必須開啟debug flag或結合@ConstructorProperties注解一起使用,詳見官方檔案(此處存疑,因為在我本地可直接使用下面這種方式,無需其他配置) -->
<constructor-arg name="str" value="https://www.cnblogs.com/shame11/p/50000"></constructor-arg>
<constructor-arg name="number" value="https://www.cnblogs.com/shame11/p/2000"></constructor-arg>
</bean>
- 基于setter方法的依賴注入:在實體化bean之后,容器會呼叫setter方法來注入依賴項
public class ExampleA {}
public class ExampleC {
private ExampleA exampleA;
//使用setter方法注入依賴項exampleA
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
}
<!-- 方法一:使用自動裝配(autowire),即容器自動去尋找依賴項來進行注入 -->
<beans ...>
<!-- 被依賴項exampleA -->
<bean id="exampleA" ></bean>
<!-- 注意,在基于xml的配置中,如要使用基于setter的自動裝配,則要指定autowire屬性為byName或byType,如果不指定,則autowire屬性默認為no,即不進行依賴注入 -->
<bean id="exampleC" autowire="byType"></bean>
</beans>
<!-- 方法二:使用手動裝配 -->
<beans ...>
<bean id="exampleA" ></bean>
<bean id="exampleC" >
<!-- 使用property標簽手動注入,name即屬性名稱,ref代表注入物件 -->
<property name="exampleA" ref="exampleA"></property>
</bean>
</beans>
(3) 在依賴注入程序中,Spring會結合PropertyEditor一起使用,來將屬性從一種型別轉換為另一種型別,我們也可以利用PropertyEditor進行資料型別轉換作業
(4) Spring對依賴項的決議規則:
- 創建ApplicationContext,并填充bean的配置元資料
- bean與bean之間的依賴關系蘊含于成員變數,建構式引數等之中,當一個bean被創建時,它的依賴項會被提供
- 每個成員變數值或建構式引數值要么是我們所提供的值,要么是對容器中另一個bean的參考
- Spring會對成員變數值或構造引數值進行確切的型別轉換,以得到實際的型別,比如 <constructor-arg value="https://www.cnblogs.com/shame11/p/2000" ... 中的值2000,Spring會根據實際情況將其轉換為字串2000或int值2000
(5) Spring在容器創建完成后便會對bean的配置元資料進行檢驗(此時不會進行依賴項的注入,直到bean被成功創建后才會進行注入),單例bean在默認情況下會被提前實體化(即在容器被創建后創建),而其他作用域的bean只有在需要的時候才會被創建(懶加載),在實際創建bean時,Spring會盡可能晚設定該bean的屬性值和依賴項,因此Spring容器可能在創建的時候正常但在之后的使用中會產生例外
(6) 在不存在回圈依賴的情況下,bean A如果有一個依賴項為bean B,則Spring容器會在呼叫bean A的setBeanB方法來注入bean B之前會完全配置好bean B,即Bean B的依賴項已被完全注入,其生命周期回呼也已被執行,如下
/**
* 提供三個類,ExampleA,ExampleB和ExampleC,其中ExampleA依賴了ExampleB,而ExampleC又依賴了ExampleA,即B->A->C
* 此時,容器會先配置好ExampleB,再注入給ExampleA,而ExampleA配置完畢后,最后會被注入給ExampleC,可觀察控制臺列印陳述句的輸出順序
*/
public class ExampleB {
public ExampleB() {
System.out.println("ExampleB構造器...");
}
}
public class ExampleA {
public ExampleA() {
System.out.println("ExampleA構造器...");
}
private ExampleB exampleB;
public void setExampleB(ExampleB exampleB) {
System.out.println("ExampleA設定了" + exampleB);
this.exampleB = exampleB;
}
}
public class ExampleC {
public ExampleC() {
System.out.println("ExampleC構造器...");
}
private ExampleA exampleA;
public void setExampleA(ExampleA exampleA) {
System.out.println("ExampleC設定了" + exampleA);
this.exampleA = exampleA;
}
}
<!-- xml組態檔 -->
<beans ...>
<bean id="exampleC" >
<property name="exampleA" ref="exampleA"></property>
</bean>
<bean id="exampleA" >
<property name="exampleB" ref="exampleB"></property>
</bean>
<bean id="exampleB" ></bean>
</beans>
(7) 依賴注入的例子:
//例一:
public class ExampleA {}
public class ExampleB {
private int year;
private ExampleA exampleA;
public void setYear(int year) {
this.year = year;
}
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
}
<!-- xml組態檔 -->
<beans ...>
<bean id="exampleA" ></bean>
<bean id="exampleB" >
<!-- 注入其他bean -->
<property name="exampleA">
<ref bean="exampleA"></ref>
</property>
<!-- 注入基本型別 -->
<property name="year" value="https://www.cnblogs.com/shame11/p/20221222"></property>
</bean>
</beans>
//例二:
public class ExampleA {
public ExampleA(int number, String str) {
}
}
public class ExampleAFactory {
public static ExampleA createInstance(int number, String str) {
ExampleA exampleA = new ExampleA(number, str);
return exampleA;
}
}
<!-- xml組態檔 -->
<beans ...>
<bean id="exampleA" factory-method="createInstance">
<!-- 使用constructor-arg標簽,來給靜態工廠方法提供引數 -->
<constructor-arg name="number" value="https://www.cnblogs.com/shame11/p/2022"></constructor-arg>
<constructor-arg name="str" value="https://www.cnblogs.com/shame11/p/12"></constructor-arg>
</bean>
</beans>
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/540497.html
標籤:Java
