最近在複習Spring
的相關內容,這篇博客就來記錄一下Spring
爲bean
的屬性注入值的四種方式。這篇博客主要講解在xml
文件中,如何爲bean
的屬性注入值,最後也會簡單提一下使用註解的方式。廢話很少說,直接開始吧。java
在Spring
中,共有四種方式爲bean
的屬性注入值,分別是:app
下面我就分別演示一下,如何使用這四種方式進行屬性的注入。ide
在演示前,咱們須要準備幾個類,我使用下面兩個類來進行注入的演示,這兩個類分別是User
和Car
類:測試
public class Car { // 只包含基本數據類型的屬性 private int speed; private double price; public Car() { } public Car(int speed, double price) { this.speed = speed; this.price = price; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } @Override public String toString() { return "Car{" + "speed=" + speed + ", price=" + price + '}'; } } public class User { private String name; private int age; // 除了上面兩個基本數據類型的屬性,User還依賴Car private Car car; public User() { } public User(String name, int age, Car car) { this.name = name; this.age = age; this.car = car; } 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 Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } }
有了上面兩個類,咱們就能夠演示set
注入了。須要注意一點,若是咱們須要使用set
注入,那麼必需要爲屬性提供set
方法,Spring
容器就是經過調用bean
的set
方法爲屬性注入值的。而在xml
文件中,使用set
注入的方式就是經過property
標籤,以下所示:this
<!-- 定義car這個bean,id爲myCar --> <bean id="myCar" class="cn.tewuyiang.pojo.Car"> <!-- 爲car的屬性注入值,由於speed和price都是基本數據類型,因此使用value爲屬性設置值; 注意,這裏的name爲speed和price,不是由於屬性名就是speed和price, 而是set方法分別爲setSpeed和setPrice,名稱是經過將set刪除,而後將第一個字母變小寫得出; --> <property name="speed" value="100"/> <property name="price" value="99999.9"/> </bean> <!-- 定義user這個bean --> <bean id="user" class="cn.tewuyiang.pojo.User"> <property name="name" value="aaa" /> <property name="age" value="123" /> <!-- car是引用類型,因此這裏使用ref爲其注入值,注入的就是上面定義的myCar 基本數據類型或Java包裝類型使用value, 而引用類型使用ref,引用另一個bean的id --> <property name="car" ref="myCar" /> </bean>
經過上面的配置,就能夠爲Car
和User
這兩個類型的bean
注入值了。須要注意的是,property的name屬性,填寫的不是屬性的名稱,而是set方法去除set,而後將第一個字符小寫後的結果。對於基本數據類型,或者是Java的包裝類型(好比String),使用value注入值,而對於引用類型,則使用ref,傳入其餘bean的id。接下來咱們就能夠測試效果了:spa
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 獲取user這個bean User user = context.getBean(User.class); // 輸出產看結果 System.out.println(user); }
因爲user
包含car
的引用,因此咱們直接輸出user
,也可以看到car
的狀況,輸入結果以下:.net
User{name='aaa', age=123, car=Car{speed=100, price=99999.9}}
下面咱們來講第二種方式——構造器注入。聽名字就能夠知道,這種注入值的方式,就是經過調用bean
所屬類的帶參構造器爲bean
的屬性注入值。這也就意味着,咱們若是須要使用構造器注入,就得爲類提供包含參數的構造方法。構造器注入,實際上有多種匹配屬性值的方式,下面咱們就來一一列舉。咱們這裏依然使用2.2
中定義的Car
和User
這兩個類,測試方法以及類的定義都不須要變,須要改變的僅僅是xml
配置文件。code
(一)匹配構造器的參數名稱xml
咱們須要經過constructor-arg
標籤爲構造器傳入參數值,可是每一個constructor-arg
標籤對應哪個參數值呢?這就有多種方式指定了。第一種就是直接匹配參數名,配置以下:對象
<bean id="myCar" class="cn.tewuyiang.pojo.Car"> <!-- 經過constructor-arg的name屬性,指定構造器參數的名稱,爲參數賦值 --> <constructor-arg name="speed" value="100" /> <constructor-arg name="price" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <constructor-arg name="name" value="aaa" /> <constructor-arg name="age" value="123" /> <!-- 和以前同樣,基本數據類型或Java包裝類型使用value, 而引用類型使用ref,引用另一個bean的id --> <constructor-arg name="car" ref="myCar" /> </bean>
這樣就完成了,測試代碼和以前同樣,運行結果也同樣,我這裏就不貼出來了。有人看完以後,可能會以爲這裏的配置和set
注入時的配置幾乎同樣,除了一個使用property
,一個使用constructor-arg
。確實,寫法上同樣,可是表示的含義卻徹底不一樣。property的name屬性,是經過set方法的名稱得來;而constructor-arg的name,則是構造器參數的名稱。
(二)匹配構造器的參數下標
上面是經過構造器參數的名稱,匹配須要傳入的值,那種方式最爲直觀,而Spring
還提供另外兩種方式匹配參數,這裏就來講說經過參數在參數列表中的下標進行匹配的方式。下面的配置,請結合2.2
節中User
和Car
的構造方法一塊兒閱讀,配置方式以下:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 下標編號從0開始,構造器的第一個參數是speed,爲它賦值100 --> <constructor-arg index="0" value="100" /> <!-- 構造器的第二個參數是price,爲它賦值99999.9 --> <constructor-arg index="1" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 與上面car的配置同理 --> <constructor-arg index="0" value="aaa" /> <constructor-arg index="1" value="123" /> <constructor-arg index="2" ref="car" /> </bean>
上面就是經過參數的下標爲構造器的參數賦值,須要注意的是,參實的下標從0開始。使用上面的方式配置,若賦值的類型與參數的類型不一致,將會在容器初始化bean
的時候拋出異常。若是bean
存在多個參數數量同樣的構造器,Spring
容器會自動找到類型匹配的那個進行調用。好比說,Car
有以下兩個構造器,Spring
容器將會調用第二個,由於上面的配置中,index = 1
對應的value
是double
類型,與第二個構造器匹配,而第一個不匹配:
public Car(double price, int speed) { this.speed = speed; this.price = price; } // 將使用匹配這個構造器 public Car(int speed, double price) { this.speed = speed; this.price = price; }
還存在另一種特殊狀況,那就是多個構造器都知足bean
的配置,此時選擇哪個?假設當前car
的配置是這樣的:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 兩個下標的value值都是整數 --> <constructor-arg index="0" value="100" /> <constructor-arg index="1" value="999"/> </bean>
假設Car
仍是有上面兩個構造器,兩個構造器都是一個int
類型一個double
類型的參數,只是位置不一樣。而配置中,指定的兩個值都是int
類型。可是,int
類型也可使用double
類型存儲,因此上面兩個構造器都是匹配的,此時調用哪個呢?結論就是調用第二個。本身去嘗試就會發現,若存在多個構造器匹配bean的定義,Spring容器老是使用最後一個知足條件的構造器。
(三)匹配構造器的參數類型
下面說最後一種匹配方式——匹配構造器的參數類型。直接看配置文件吧:
<bean id="car" class="cn.tewuyiang.pojo.Car"> <!-- 使用type屬性匹配類型,car的構造器包含兩個參數,一個是int類型,一個是double類型 --> <constructor-arg type="int" value="100" /> <constructor-arg type="double" value="99999.9"/> </bean> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 對於引用類型,須要使用限定類名 --> <constructor-arg type="java.lang.String" value="aaa" /> <constructor-arg type="int" value="123" /> <constructor-arg type="cn.tewuyiang.pojo.Car" ref="car" /> </bean>
上面應該不難理解,直接經過匹配構造器的參數類型,從而選擇一個可以徹底匹配的構造器,調用這個構造器完成bean
的建立和屬性注入。須要注意的是,上面的配置中,類型並不須要按構造器中聲明的順序編寫,Spring
也能進行匹配。這也就意味着可能出現多個可以匹配的構造器,和上一個例子中同樣。好比說,Car
仍是有下面兩個構造器:
public Car(double price, int speed) { // 輸出一句話,看是否調用這個構造器 System.out.println(111); this.speed = speed; this.price = price; } // 將使用匹配這個構造器 public Car(int speed, double price) { // 輸出一句話,看是否調用這個構造器 System.out.println(222); this.speed = speed; this.price = price; }
上面兩個構造器都是一個int
,一個double
類型的參數,都符合xml文件中,car
這個bean
的配置。經過測試發現,Spring容器使用的永遠都是最後一個符合條件的構造器,這和上面經過下標匹配是一致的。須要說明的一點是,這三種使用構造器注入的方式,能夠混用。
靜態工廠注入就是咱們編寫一個靜態的工廠方法,這個工廠方法會返回一個咱們須要的值,而後在配置文件中,咱們指定使用這個工廠方法建立bean
。首先咱們須要一個靜態工廠,以下所示:
public class SimpleFactory { /** * 靜態工廠,返回一個Car的實例對象 */ public static Car getCar() { return new Car(12345, 5.4321); } }
下面咱們須要在xml
中配置car這個bean,並指定它由工廠方法進行建立。配置以下:
<!-- 注意,這裏的配置並非建立一個SimpleFactory對象,取名爲myCar, 這一句配置的意思是,調用SimpleFactory的getCar方法,建立一個car實例對象, 將這個car對象取名爲myCar。 --> <bean id="car" class="cn.tewuyiang.factory.SimpleFactory" factory-method="getCar"/> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- name和age使用set注入 --> <property name="name" value="aaa"/> <property name="age" value="123"/> <!-- 將上面配置的car,注入到user的car屬性中 --> <property name="car" ref="car"/> </bean>
以上就配置成功了,測試方法以及執行效果以下,注意看car
的屬性值,就是咱們在靜態工廠中配置的那樣,這說明,Spring
容器確實是使用咱們定義的靜態工廠方法,建立了car
這個bean
:
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 獲取靜態工廠建立的car Car car = (Car) context.getBean("car"); // 獲取user User user = context.getBean(User.class); System.out.println(car); System.out.println(user); }
輸出以下所示:
Car{speed=12345, price=5.4321} User{name='aaa', age=123, car=Car{speed=12345, price=5.4321}}
實例工廠與靜態工廠相似,不一樣的是,靜態工廠調用工廠方法不須要先建立工廠類的對象,由於靜態方法能夠直接經過類調用,因此在上面的配置文件中,並無聲明工廠類的bean
。可是,實例工廠,須要有一個實例對象,才能調用它的工廠方法。咱們先看看實例工廠的定義:
public class SimpleFactory { /** * 實例工廠方法,返回一個Car的實例對象 */ public Car getCar() { return new Car(12345, 5.4321); } /** * 實例工廠方法,返回一個String */ public String getName() { return "tewuyiang"; } /** * 實例工廠方法,返回一個int,在Spring容器中會被包裝成Integer */ public int getAge() { return 128; } }
在上面的工廠類中,共定義了三個工廠方法,分別用來返回user
所需的car
,name
以及age
,而配置文件以下:
<!-- 聲明實例工廠bean,Spring容器須要先建立一個SimpleFactory對象,才能調用工廠方法 --> <bean id="factory" class="cn.tewuyiang.factory.SimpleFactory" /> <!-- 經過實例工廠的工廠方法,建立三個bean,經過factory-bean指定工廠對象, 經過factory-method指定須要調用的工廠方法 --> <bean id="name" factory-bean="factory" factory-method="getName" /> <bean id="age" factory-bean="factory" factory-method="getAge" /> <bean id="car" factory-bean="factory" factory-method="getCar" /> <bean id="user" class="cn.tewuyiang.pojo.User"> <!-- 將上面經過實例工廠方法建立的bean,注入到user中 --> <property name="name" ref="name"/> <property name="age" ref="age"/> <property name="car" ref="car"/> </bean>
咱們嘗試從Spring
容器中取出name
,age
,car
以及user
,看看它們的值,測試代碼以下:
@Test public void test1() { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 獲取靜態工廠建立的car,name和age這三個bean Car car = (Car) context.getBean("car"); String name = (String) context.getBean("name"); Integer age = (Integer) context.getBean("age"); // 獲取user這個bean User user = context.getBean(User.class); System.out.println(car); System.out.println(name); System.out.println(age); System.out.println(user); }
如下就是輸出結果,能夠看到,咱們經過工廠建立的bean
,都在Spring
容器中可以獲取到:
Car{speed=12345, price=5.4321} tewuyiang 128 User{name='tewuyiang', age=128, car=Car{speed=12345, price=5.4321}}
假如須要使用註解的方式爲bean
注入屬性值,應該這麼操做呢?首先,若是bean
依賴於其餘bean
(好比User
依賴Car
),那麼咱們可使用@Autowired
或者@Resource
這兩個註解進行依賴注入,這個你們應該都知道。可是若是要爲基本數據類型或者是Java
的封裝類型(好比String
)賦值呢?這時候可使用@Value
註解。這裏我就不演示了,感興趣的能夠自行去研究,應該是比xml
的方式簡單多了。
以上就對Spring
基於xml
配置文件進行屬性注入的方式作了一個還算詳細的介紹。其實這一部分的內容仍是比較基礎,畢竟只是Spring
的使用,並不涉及原理,只要本身嘗試寫一遍就瞭解了。若以上內容存在錯誤或不足,歡迎指正,共同進步。也但願以上內容對須要的人有所幫助。