1. 程式人生 > >Spring框架之自動裝配

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之間的耦合降低到程式碼層次,不利於高層次解耦。

以上內容僅供大家學習參考,寫得不好,請見諒,如有錯誤,請指出,謝謝!