Spring使用注解進行對象裝配(DI)的操作
一. 什么是對象裝配
獲取 Bean 對象也叫做對象裝配,就是把對象取出來放到某個類中,有時候也叫對象注入。對象裝配(對象注入)的實現(xiàn)方法以下 3 種:
- 屬性注入 ,就是將對象注入到某個類的一個屬性當中。
- 構造方法注入 ,就是通過構造方法來將對象注入到類中。
- Setter 注入 ,通過 SetXXX 系列方法將對象注入到類中。
常見有關對象注入的注解有兩個,一個是@Autowired
,另外一個是@Resource
??它們兩者的區(qū)別如下:
- 出身不同:@Autowired 是由Spring提供的,而 @Resource 是JDK提供的。
- 查找順序不同:從容器中獲取對象時 @Autowired 先根據(jù)類型再根據(jù)名稱查詢,而 @Resource 先根據(jù)名稱再根據(jù)類型查詢。
- 使?時設置的參數(shù)不同:@Resource 支持更多的參數(shù)設置(有 7 個),如name、type等,而@Autowired只支持設置required一個參數(shù),用來設置注入 Bean 的時候該 Bean 是否必須存在(true/false)。
- 依賴注入支持不同:@Autowired 支持屬性注入,構造方法注入和 Setter 注入,而 @Resource 只支持屬性注入和 Settter 注入,但是不支持構造方法注入。
- 對 IDEA 的兼容性支持不同:使用 @Autowired 在 IDEA 旗艦版下可能會有誤報(設置required即可);而 @Resource 不存在誤報的問題。
二. 三種注入方式
1. 屬性注入
屬性注入只需要在需要注入對象的屬性上加上 @Autowired 或者 @Resource 注解就可以了,這里以 @Autowired 為例。
首先來看第一種情況,待注入的同類對象只有一個,此時我們直接使用 @Autowired 注解就好,不必設置參數(shù),例如我們在UserController
類里面注入UserService
對象。
下面UserService
的結構,先使用 @Service 將 Bean 存放到 Spring 中:
屬性注入:
package com.tr.demo.controller; import com.tr.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController { //屬性注入 @Autowired private UserService userService; public void sayHi() { userService.sayHi(); } }
此時我們就可以在啟動類中,使用上下文對象來獲取到UserController
對象,通過執(zhí)行UserController
對象的sayHi
方法來進而調(diào)用到注入的UserService
對象中的sayHi
方法了,此時的UserService
對象就不是我們自己new
出來的了。
import com.tr.demo.controller.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); UserController usercontroller = context.getBean("userController", UserController.class); usercontroller.sayHi(); } }
運行結果:
上面說的是同類對象只有一個的情況,而如果存在多個同類對象,我們就得通過參數(shù)來告知容器我們要注入哪一個對象,不告知就會報錯。
比如我們將多個User
對象添加到容器中,如下:
package com.tr.demo.model; // User 結構 public class User { private int id; private String name; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } package com.tr.demo.model; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class UserBeans { // 使用方法注解添加多個 User 對象到容器中 @Bean("user1") public User user1(){ User user = new User(); user.setId(1); user.setName("張三"); return user; } @Bean("user2") public User user2(){ User user = new User(); user.setId(2); user.setName("李四"); return user; } @Bean("user3") public User user3(){ User user = new User(); user.setId(3); user.setName("王五"); return user; } }
而在UserController2
類中需要注入User
對象,此時我們運行程序就會報錯:
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController2 { @Autowired private User user; public void sayHi() { System.out.println("Hello, " + user); } }
我們試著以同樣的方法來調(diào)用sayHi
方法:
運行結果:
@Autowired 依賴注入流程首先是先根據(jù)類型從容器中獲取對象,如果只能獲取到一個,那么就直接將此對象注入到當前屬性上;如果能獲取到多個對象,此時會使用 BeanName
進行匹配,而我們添加到 Spring 中的對象是沒有一個叫user
的,所以程序就報錯了。
此時就需要我們來告知容器我們需要哪一個具體的 Bean,要獲得目標對象主要有下面三種方法:
1??方法1:將屬性的變量名設置為你需要的那個BeanName
就可以了,后面的構造方法與 Setter 注入同理,將形參名設置成與BeanName
相同即可。
2??方法2:@Autowired 注解與 @Qualifier 注解配合使用,設置 @Qualifier 的value
參數(shù)為BeanName
即可,要注意 @Qualifier 注解不能修飾方法,只能修飾變量。
3??方法3:將 @Autowired 注解替換成 @Resource 注解的,并將它name
參數(shù)值設置為BeanName
即可。
2. 構造方法注入
在構造方法加上 @Autowired 注解就可,要注意 @Resource 注解是不支持構造方法注入的,我們就直接演示如何獲取取多個同類對象中的其中一個了,還是用上面添加到容器中的多個 User 對象。
方法1:將構造方法形參名設置為user1
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class UserController3 { private User user; @Autowired public UserController3(User user1) { this.user = user1; } public void sayHi() { System.out.println("Hello, " + user); } }
啟動類就不貼代碼了,一樣的,運行結果如下:
方法2:@Autowired 搭配 @Qualifier
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @Controller public class UserController4 { private User user; @Autowired public UserController4(@Qualifier(value = "user2") User user) { this.user = user; } public void sayHi() { System.out.println("Hello, " + user); } }
運行結果:
對了,如果一個類中只有一個構造方法,@Autowired 是可以省略的,演示一下:
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.stereotype.Controller; @Controller public class UserController5 { private User user; public UserController5(User user3) { this.user = user3; } public void sayHi() { System.out.println("Hello, " + user); } }
此時仍然是可以成功注入對象。
如果有多個構造方法,要注意此時是不能省略 @Autowired 的,會導致會注入對象失敗。
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.stereotype.Controller; @Controller public class UserController6 { private User user; public UserController6(User user1) { this.user = user1; } public UserController6() { System.out.println("調(diào)用無參構造"); } public void sayHi() { System.out.println("Hello, " + user); } }
此時可以看到注入對象失敗了,輸出的結果是null
。
當然此時加上 @Autowired 注解就能正常注入了,就不做展示了。
3. Setter注入
Setter 注入就是在 setXXX 系列方法上加上 @Resource 或者 @Autowired 進行注入,和構造方法注入大同小異,簡單演示一下。
package com.tr.demo.controller; import com.tr.demo.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; @Controller public class UserController7 { private User user; @Autowired public void setUser(@Qualifier(value = "user2") User user) { this.user = user; } public void sayHi() { System.out.println("Hello, " + user); } }
啟動類和運行結果:
這里這里第一行輸入的是因為啟動程序會將上面寫的UserController6
也添加到容器中,UserController6
的無參構造方法是我們自定義的。
三. 三種注入方式的優(yōu)缺點
在早期的 Spring 版本中,官方推薦使用的 Setter 注入,最開始說的原因就是不符合單一設計原則吧,而現(xiàn)在比較新的 Spring 版本(Sring 4.x 之后)中,官方最使用推薦的又是構造方法注入了,說法是因為它的通用性最好。
??屬性注入
優(yōu)點:
- 使用起來簡單方便
缺點:
- 無法注入到一個
final
修飾的變量,因為 final 修飾的變量只有兩種賦值方式,一是直接賦值,二是通過構造方法進行賦值,而屬性注入這兩種方式都不能滿足。
2.通用性問題,屬性注入只能在 IoC 容器中使用,在非 IoC 容器中是不可?的。
3.更容易違背單一設計原則,簡單理解就是注入方式越簡單,濫用的概率越大,就比如在數(shù)據(jù)持久層有一個針對用戶操作的類,本來這個類就只是注入用戶相關操作的依賴就行了,但由于屬性注入使用起來成本不高,程序猿就多注了一些依賴去完成了一些和用戶操作無關的內(nèi)容,這就違背了單一設計原則了。
??Setter 注入
優(yōu)點:
- 通常情況下,setXXX 系列的方法中只會設置一個屬性,就更符合單一設計原則。
缺點:
- 同樣的,也不能注入到一個 final 修飾的變量中。
- 注入的對象是可能被修改的,因為 setXXX 系列的方法隨時都有可能被調(diào)用導致注入的 Bean 就被修改了。
??構造方法注入
優(yōu)點:
可以注入到一個被 final 修飾的變量。
- 注入對象不會被修改,因為構造方法只會在對象創(chuàng)建時執(zhí)行一次,不存在注入對象被隨時修改的情況。
- 可以保證注入對象的完全初始化,因為構造方法是在對象創(chuàng)建之前執(zhí)行的。
- 通用性最好,因為不管你怎么寫 Java 代碼,創(chuàng)建實例對象時都要執(zhí)行構造方法吧。
缺點:
- 相較于屬性注入,寫法更加復雜,如果有多個注?會顯得?較臃腫,但出現(xiàn)這種情況你應該考慮?下當前類是否符合程序的單?職責的設計模式了。
- 使用構造注入,無法解決循環(huán)依賴的問題。
四. 綜合練習
在 Spring 項?中,通過 main ?法獲取到 Controller 類,調(diào)? Controller ??通過注?的?式調(diào)? Service 類,Service 再通過注?的?式獲取到 Repository 類,Repository 類??有?個?法構建? 個 User 對象,返回給 main ?法。Repository ?需連接數(shù)據(jù)庫,使?偽代碼即可。
首先要清楚的是在 main 方法中是不能使用依賴注入的,因為類的靜態(tài)部分是在 Spring 注入之前的加載的,仔細想一下,在類加載時就要使用一個還沒注入的對象這是不現(xiàn)實的。
所以我們要在 main 中執(zhí)行的是將掃描路徑中的類添加到 Spring 中,對象的注入要在 mian 方法所在類的外部去實現(xiàn)。
運行結果:
到此這篇關于Spring使用注解進行對象裝配(DI)的文章就介紹到這了,更多相關spring對象裝配內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式
這篇文章主要介紹了SpringMVC框架中使用Filter實現(xiàn)請求日志打印方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10在Eclipse IDE使用Gradle構建應用程序(圖文)
這篇文章主要介紹了在Eclipse IDE使用Gradle構建應用程序(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12Java GUI編程之貪吃蛇游戲簡單實現(xiàn)方法【附demo源碼下載】
這篇文章主要介紹了Java GUI編程之貪吃蛇游戲簡單實現(xiàn)方法,詳細分析了貪吃蛇游戲的具體實現(xiàn)步驟與相關注意事項,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-09-09Springboot maven plugin插件原理及作用
這篇文章主要介紹了Springboot maven plugin插件原理及作用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10