1. 程式人生 > >Spring中bean的四種注入方式

Spring中bean的四種注入方式

# 一、前言   最近在複習``Spring``的相關內容,這篇部落格就來記錄一下``Spring``為``bean``的屬性注入值的四種方式。這篇部落格主要講解在``xml``檔案中,如何為``bean``的屬性注入值,最後也會簡單提一下使用註解的方式。廢話不多說,直接開始吧。
# 二、正文 ## 2.1 注入方式   在``Spring``中,共有四種方式為``bean``的屬性注入值,分別是: - **set方法注入** - **構造器注入** - **靜態工廠注入** - **例項工廠注入**   下面我就分別演示一下,如何使用這四種方式進行屬性的注入。
## 2.2 set方法注入   在演示前,我們需要準備幾個類,我使用下面兩個類來進行注入的演示,這兩個類分別是``User``和``Car``類: ```java 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``標籤,如下所示: ```xml
```   通過上面的配置,就可以為``Car``和``User``這兩個型別的``bean``注入值了。需要注意的是,**property的name屬性,填寫的不是屬性的名稱,而是set方法去除set,然後將第一個字元小寫後的結果。對於基本資料型別,或者是Java的包裝型別(比如String),使用value注入值,而對於引用型別,則使用ref,傳入其他bean的id。**接下來我們就可以測試效果了: ```java @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``的情況,輸入結果如下: ```java User{name='aaa', age=123, car=Car{speed=100, price=99999.9}} ```
## 2.3 構造器注入   下面我們來說第二種方式——構造器注入。聽名字就可以知道,這種注入值的方式,就是通過呼叫``bean``所屬類的帶參構造器為``bean``的屬性注入值。這也就意味著,**我們如果需要使用構造器注入,就得為類提供包含引數的構造方法**。構造器注入,實際上有多種匹配屬性值的方式,下面我們就來一一列舉。我們這裡依然使用``2.2``中定義的``Car``和``User``這兩個類,測試方法以及類的定義都不需要變,需要改變的僅僅是``xml``配置檔案。 **(一)匹配構造器的引數名稱**   我們需要通過``constructor-arg``標籤為構造器傳入引數值,但是每個``constructor-arg``標籤對應哪一個引數值呢?這就有多種方式指定了。第一種就是直接匹配引數名,配置如下: ```xml
```   這樣就完成了,測試程式碼和之前一樣,執行結果也一樣,我這裡就不貼出來了。有人看完之後,可能會覺得這裡的配置和``set``注入時的配置幾乎一樣,除了一個使用``property``,一個使用``constructor-arg``。確實,寫法上一樣,但是表示的含義卻完全不同。**property的name屬性,是通過set方法的名稱得來;而constructor-arg的name,則是構造器引數的名稱**。
**(二)匹配構造器的引數下標**   上面是通過構造器引數的名稱,匹配需要傳入的值,那種方式最為直觀,而``Spring``還提供另外兩種方式匹配引數,這裡就來說說通過引數在引數列表中的下標進行匹配的方式。下面的配置,請結合``2.2``節中``User``和``Car``的構造方法一起閱讀,配置方式如下: ```xml
```   上面就是通過引數的下標為構造器的引數賦值,需要注意的是,**參實的下標從0開始**。使用上面的方式配置,若賦值的型別與引數的型別不一致,將會在容器初始化``bean``的時候丟擲異常。如果``bean``存在多個引數數量一樣的構造器,``Spring``容器會自動找到型別匹配的那個進行呼叫。比如說,``Car``有如下兩個構造器,``Spring``容器將會呼叫第二個,因為上面的配置中,``index = 1``對應的``value``是``double``型別,與第二個構造器匹配,而第一個不匹配: ```java 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``的配置是這樣的: ```xml ```   假設``Car``還是有上面兩個構造器,兩個構造器都是一個``int``型別一個``double``型別的引數,只是位置不同。而配置中,指定的兩個值都是``int``型別。但是,``int``型別也可以使用``double``型別儲存,所以上面兩個構造器都是匹配的,此時呼叫哪一個呢?結論就是呼叫第二個。自己去嘗試就會發現,**若存在多個構造器匹配bean的定義,Spring容器總是使用最後一個滿足條件的構造器**。
**(三)匹配構造器的引數型別**   下面說最後一種匹配方式——匹配構造器的引數型別。直接看配置檔案吧: ```xml ```   上面應該不難理解,直接通過匹配構造器的引數型別,從而選擇一個能夠完全匹配的構造器,呼叫這個構造器完成``bean``的建立和屬性注入。需要注意的是,上面的配置中,型別並不需要按構造器中宣告的順序編寫,``Spring``也能進行匹配。這也就意味著可能出現多個能夠匹配的構造器,和上一個例子中一樣。比如說,``Car``還是有下面兩個構造器: ```java 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容器使用的永遠都是最後一個符合條件的構造器**,這和上面通過下標匹配是一致的。**需要說明的一點是,這三種使用構造器注入的方式,可以混用**。
## 2.4 靜態工廠注入   靜態工廠注入就是我們編寫一個靜態的工廠方法,這個工廠方法會返回一個我們需要的值,然後在配置檔案中,我們指定使用這個工廠方法建立``bean``。首先我們需要一個靜態工廠,如下所示: ```java public class SimpleFactory { /** * 靜態工廠,返回一個Car的例項物件 */ public static Car getCar() { return new Car(12345, 5.4321); } } ```   下面我們需要在``xml``中配置car這個bean,並指定它由工廠方法進行建立。配置如下: ```xml ```   以上就配置成功了,測試方法以及執行效果如下,注意看``car``的屬性值,就是我們在靜態工廠中配置的那樣,這說明,``Spring``容器確實是使用我們定義的靜態工廠方法,建立了``car``這個``bean``: ```java @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); } ```   輸出如下所示: ```java Car{speed=12345, price=5.4321} User{name='aaa', age=123, car=Car{speed=12345, price=5.4321}} ```
## 2.5 例項工廠注入   例項工廠與靜態工廠類似,不同的是,靜態工廠呼叫工廠方法不需要先建立工廠類的物件,因為靜態方法可以直接通過類呼叫,所以在上面的配置檔案中,並沒有宣告工廠類的``bean``。但是,例項工廠,需要有一個例項物件,才能呼叫它的工廠方法。我們先看看例項工廠的定義: ```java 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``,而配置檔案如下: ```xml ```   我們嘗試從``Spring``容器中取出``name``,``age``,``car``以及``user``,看看它們的值,測試程式碼如下: ```java @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``容器中能夠獲取到: ```java Car{speed=12345, price=5.4321} tewuyiang 128 User{name='tewuyiang', age=128, car=Car{speed=12345, price=5.4321}} ```
## 2.6 使用註解注入   假如需要使用註解的方式為``bean``注入屬性值,應該這麼操作呢?首先,如果``bean``依賴於其他``bean``(比如``User``依賴``Car``),那麼我們可以使用``@Autowired``或者``@Resource``這兩個註解進行依賴注入,這個大家應該都知道。但是如果要為基本資料型別或者是``Java``的封裝型別(比如``String``)賦值呢?這時候可以使用``@Value``註解。這裡我就不演示了,感興趣的可以自行去研究,應該是比``xml``的方式簡單多了。
# 三、總結   以上就對``Spring``基於``xml``配置檔案進行屬性注入的方式做了一個還算詳細的介紹。其實這一部分的內容還是比較基礎,畢竟只是``Spring``的使用,並不涉及原理,只要自己嘗試寫一遍就瞭解了。若以上內容存在錯誤或不足,歡迎指正,共同進步。也希望以上內容對需要的人有所幫助。
# 四、參考 - -