Spring框架之自動裝配
Spring的IoC容器通過Java反射機制瞭解了容器中所存在Bean的配置資訊,這包括構造方法的結構,屬性的資訊,而正是由於這個原因,Spring容器才能通過某種規則來對Bean進行自動裝配,而無須通過顯式的方法進行配置。
一.自動裝配型別:Spring IoC容器可以自動裝配相互協作Bean之間的關聯關係。因此,可以自動使Spring通過檢查BeanFactory中的內容,來指定Bean協作(其它被依賴的Bean),,下面來介紹這4種類型:
1.byName型別:根據屬性名自動裝配。此型別將檢查容器並根據名字查詢與屬性完全一樣的bean,並將其與屬性自動裝配。
注:使用byName自動裝配型別時,對於設定的屬性名字必須提供set()方法,否則在啟動Spring時,將會報出異常。
下面附上一個例子:
(1).第一步,新建一個Java專案,專案名為spring_byName,然後配置好spring環境即可。
(2).第二步,新建一個People類,放在com.bean包下,宣告三個屬性,分別為name,age,sex,並生成其setXxx()和getXxx()方法,在寫一個方法get(),用於輸出People各個屬性值,具體看程式碼,如下:
<pre class="java" name="code">package com.bean; public class People { private String name; private int age; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public void get() { System.out.print("姓名為:" + name); System.out.print(",年齡為:" + age); System.out.print(",性別為:" + sex); } }
(3).第三步,新建一個Student類,也放在com.bean包下,宣告course課程這個屬性以及上面People類的物件屬性,並生成屬性的setXxx()和getXxx()方法,在寫一個get()方法,輸出course這個屬性值和呼叫People類中的get()方法,具體程式碼如下:
package com.bean; public class Student { private String course; private People people; public String getCourse() { return course; } public void setCourse(String course) { this.course = course; } public People getPeople() { return people; } public void setPeople(People people) { this.people = people; } public void get() { System.out.println("學生的課程為:" + course); people.get(); } }
(4).第四步,開啟配置檔案applicationContext.xml檔案,只需要在<bean>標籤中通過autowire屬性設定為byName來啟動自動裝配,如下程式碼所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="people" class="com.bean.People">
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
<bean id="student" class="com.bean.Student" autowire="byName">
<property name="course" value="Java程式設計"></property>
</bean>
</beans>
其中使用<property>標籤中的所指定的name要與類中的屬性名一一對應起來,然後在student這個bean裡使用了byName自動裝配型別,那麼在peoplebean裡的id屬性值必須與Student類中所宣告的People物件屬性名一致!
(5).第五步,編寫一個測試類Test,也放在com.bean包下,其中獲取載入配置檔案,然後通過ApplicationContext物件獲取Student這個Bean,再呼叫這個Bean物件的get()方法,具體程式碼如下:
package com.bean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args){
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Student stu=(Student) ac.getBean("student");
stu.get();
}
}
(6).第六步,執行效果如下:
這樣便把People這個Bean的三個屬性值自動裝配進去了Student這個Bean中去了,所以呼叫get()方法也把People類的屬性值也輸出來了!
(7).如果我們把配置檔案中的People這個Bean改成下面所示,即把id屬性改成people1,如下:
<bean id="people1" class="com.bean.People">
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
其它程式碼不變,執行測試類Test之後,如下圖所示:
這樣就報空指標異常了,獲取不到People類的屬性名,所以使用byName自動裝配型別的話,一個Bean中的id要與另一個Bean的物件屬性名一一對應!
注:如果上面這個例子把自動裝配型別改為byType的話,也是可以的,就算你People類在配置檔案裡配置bean的id屬性任意都行,因為此時就是按照型別裝配了!
2.byType型別:如果容器存在一個與指定屬性型別相同的bean,那麼將與該屬性自動裝配;如果存在多個該型別的bean,那麼丟擲異常,並指出不能使用byType方式進行自動裝配;如果沒找到相匹配的bean,則什麼事都不發生。
(1).這裡就不附上例子了,我們通過上面這個例子把autowire屬性設定為byType,也可以執行成功,因為是按照型別匹配的,我們可以把配置檔案改成如下所示,其它都不改:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="p" class="com.bean.People">
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
<bean id="student" class="com.bean.Student" autowire="byType">
<property name="course" value="Java程式設計"></property>
</bean>
</beans>
執行之後效果與byName一樣。
(2).如果我們把配置檔案改成如下所示,我們配置兩個People類,如下圖所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="p" class="com.bean.People">
<property name="name" value="張三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>
<bean id="p1" class="com.bean.People">
<property name="name" value="李紅"></property>
<property name="age" value="18"></property>
<property name="sex" value="女"></property>
</bean>
<bean id="student" class="com.bean.Student" autowire="byType">
<property name="course" value="Java程式設計"></property>
</bean>
</beans>
則會報一個錯誤,因為我們定義了兩個型別都為People的bean,所以不能匹配到是哪一個bean,報錯資訊如下圖所示:
即如果Bean採用byType進行自動裝配,當IoC容器中存在多個型別匹配的Bean時,就無法判斷究竟該選擇哪個Bean作為自動裝配的目標,所以就丟擲上面的異常資訊了!
(3).這裡簡單講述一下Spring如何進行匹配入參,如果A類和B類,兩者滿足以下三種情況中的任何一種,可以稱之A按型別匹配於B:
~A和B是相同的型別。 ~A是B的子類。 ~A實現了B的介面。
3.constructor型別:與byType型別相似,不同在於constructor型別應用於構造器引數。如果容器中沒有找到與構造器引數型別一致的bean,那麼丟擲異常。
(1).其實使用constructor自動裝配時,只不過是通過構造方法而進行自動裝配的。
(2).下面附上一個例子:
第一步,首先建立一個Java專案,專案名稱為spring_constructor,配置好Spring環境。
第二步,新建一個Man類,具體程式碼如下,就不做分析了,很簡單:
package com.bean;
public class Man {
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void out(){
System.out.println(name);
System.out.println(sex);
System.out.println(age);
}
}
第三步,新建一個Building類,程式碼如下:
package com.bean;
public class Building {
private String name;
private int floors;
private Man m;
public Building(String name,int floors,Man m){
this.name=name;
this.floors=floors;
this.m=m;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getFloors() {
return floors;
}
public void setFloors(int floors) {
this.floors = floors;
}
public Man getPerson() {
return m;
}
public void setPerson(Man man) {
this.m = man;
}
public void out(){
System.out.println(name);
System.out.println(floors);
m.out();
}
}
Building類裡有一個帶三個引數的構造方法。
第四步,開啟配置檔案進行配置,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="man" class="com.bean.Man">
<property name="name" value="A-Lc"></property>
<property name="sex" value="男"></property>
<property name="age" value="23"></property>
</bean>
<bean id="building" class="com.bean.Building" autowire="constructor">
<constructor-arg index="0" value="金大福"></constructor-arg>
<constructor-arg index="1" value="88"></constructor-arg>
</bean>
</beans>
在配置檔案中,把autowire屬性設定為constructor來啟動自動裝配,然後再用<constructor-arg>標籤把帶的引數傳進去,這裡只傳了兩個,其中最後一個Man的物件屬性引數通過匹配有Man型別的bean傳進去。
第五步,新建Test測試類,程式碼如下:
package com.bean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bean.Building;
public class Test {
public static void main(String[] args){
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
Building b=(Building) ac.getBean("building");
b.out();
}
}
第六步,執行後效果如下:
二.自動裝配控制
1.在一個Spring應用中,Bean的數量很多,因此在使用自動裝配時,如果容器中多個匹配項,Spring會丟擲異常,不能正常工作。針對這種問題,可以對那些不需要匹配的Bean進行設定,設定這個Bean是否為被自動裝配物件。當採用XML格式配置Bean時,可將<bean>元素的autowire-candidate屬性設定為false,這樣容器在查詢自動裝配物件時將不考慮該Bean,也就是這個Bean將不會被作為自動裝配物件。
2.如果我們把上面constructor型別的自動裝配例子的配置檔案修改為下面所示,其它程式碼不改:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="man" class="com.bean.Man">
<property name="name" value="A-Lc"></property>
<property name="sex" value="男"></property>
<property name="age" value="23"></property>
</bean>
<bean id="man1" class="com.bean.Man">
<property name="name" value="A-Xg"></property>
<property name="sex" value="男"></property>
<property name="age" value="23"></property>
</bean>
<bean id="building" class="com.bean.Building" autowire="constructor">
<constructor-arg index="0" value="金大福"></constructor-arg>
<constructor-arg index="1" value="88"></constructor-arg>
</bean>
</beans>
定義兩個型別相同的bean,此時IoC容器不知道匹配哪一個 ,所以會報下圖的錯:
3.我們可以將上面的程式碼的一個不需要匹配的bean的autowire-candidate屬性設定為false,如下程式碼所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="man" class="com.bean.Man" autowire-candidate="false">
<property name="name" value="A-Lc"></property>
<property name="sex" value="男"></property>
<property name="age" value="23"></property>
</bean>
<bean id="man1" class="com.bean.Man">
<property name="name" value="A-Xg"></property>
<property name="sex" value="男"></property>
<property name="age" value="23"></property>
</bean>
<bean id="building" class="com.bean.Building" autowire="constructor">
<constructor-arg index="0" value="金大福"></constructor-arg>
<constructor-arg index="1" value="88"></constructor-arg>
</bean>
</beans>
4.此時執行效果就輸出姓名為A-Xg的資訊了,如下圖所示:
三.使用自動裝配的前提:使用自動裝配,主要是為了方便,提高工作效率,但是要正確併合理地使用自動裝配,必須先理解自動裝配的優缺點,才能正確判斷何時需要使用自動裝配。
1.自動裝配的優點如下:
(1).自動裝配能顯著減少裝配的數量,因此在配置數量相當多時採用自動裝配,可以減少工作量。
(2).自動裝配可以使配置與Java程式碼同步更新。例如:如果需要給一個Java類增加一個依賴,那麼該依賴將自動實現而不需要修改配置。因此強烈在開發過程中採用自動裝配,而在系統趨於穩定的時候改為顯式裝配的方式。
2.雖然自動裝配具有上面這些優點,但不是說什麼時候都可以使用它,因為它還有如下一些缺點:
(1).儘管自動裝配比顯式裝配更神奇,但是,Spring會盡量避免在裝配不明確時進行猜測,因為裝配不明確可能出現難以預料的結果,而Spring所管理的物件之間的關聯關係也不再能清晰地進行文件化。
(2).對於那些根據Spring配置檔案生成文件的工具來說,自動裝配將會使這些工具無法生成依賴資訊。
3.決定是否使用自動裝配方式時,沒有絕對的對錯。考慮專案的實際是最好的方法,例如對於大型的應用,不建議使用自動裝配。雖然自動裝配可以減少配置檔案的工作量,但是大大降低了依賴關係的清晰度和透明度。由於依賴關係的配置基於原始檔的屬性名,這就導致Bean與Bean之間的耦合降低到程式碼層次,不利於高層次解耦。
以上內容僅供大家學習參考,寫得不好,請見諒,如有錯誤,請指出,謝謝!