-
1 # 程式猿W
-
2 # CHENG142857
先說結論吧,在絕大部分情況下,使用這2個註解的任何一個都可以實現自動裝配,在使用方面是無感的。
@Resource註解是javax包的,@Autowired是Spring宣告的;@Resource會優先byName來裝配,即使名稱不匹配,也會嘗試byType來裝配;@Autowired則是先查詢匹配型別物件的數量,如果有且僅有1個,則直接裝配,如果為0個,則無法裝配,此時需要考慮該註解的required屬性,預設是true,就會因為“必須裝配卻沒有匹配型別的物件”而丟擲異常,如果顯式的設定為false,則不裝配,保持被自動裝配的屬性為null值,如果匹配型別的物件超過1個,則會嘗試byName,如果成功,則裝配,如果失敗,則丟擲異常。另外,還有些關於不推薦使用@Autowired註解的說法,大致解釋一下:
-
3 # Java架構達人
spring中,@Resource和@Autowired都是做bean的注入時使用。使用過程中,有時候@Resource 和 @Autowired可以替換使用;有時,則不可以。
下面,根據自己的學習,整理下這兩個註解使用中的共同點和不同點,及用法上的不同。
共同點
@Resource和@Autowired都可以作為注入屬性的修飾,在介面僅有單一實現類時,兩個註解的修飾效果相同,可以互相替換,不影響使用。
不同點
@Resource是Java自己的註解,@Resource有兩個屬性是比較重要的,分是name和type;Spring將@Resource註解的name屬性解析為bean的名字,而type屬性則解析為bean的型別。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將透過反射機制使用byName自動注入策略。
@Autowired是spring的註解,是spring2.5版本引入的,Autowired只根據type進行注入,不會去匹配name。如果涉及到type無法辨別注入物件時,那需要依賴@Qualifier或@Primary註解一起來修飾。
我們建立一個簡單的springboot專案demo,
定義一個介面Human.java,裡面一個方法 runMarathon,
一個實現類Man.java
一個Controller類HumanController.java,裡面注入Human介面的實現
附各Java類原始碼
package com.example.annotation.service;
/**
* service介面定義
* @author Administrator
*/
public interface Human {
/**
* 跑馬拉松
* @return
*/
String runMarathon();
}
package com.example.annotation.service.impl;
import com.example.annotation.service.Human;
import org.springframework.stereotype.Service;
/**
* service介面第一實現類
* @author Administrator
*/
@Service
public class Man implements Human {
public String runMarathon() {
return "A man run marathon";
}
}
package com.example.annotation.controller;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.annotation.service.Human;
/**
* controller層實現類
* @author Administrator
*/
@RestController
@RequestMapping("/an")
public class HumanController {
@Resource
private Human human;
@RequestMapping("/run")
public String runMarathon() {
return human.runMarathon();
}
}
至此,程式碼整理完成,啟動springboot,瀏覽器位址列輸入http://localhost:8080/an/run
改動一:
將HumanController.java 類中的註解替換為@Autowired,再次啟動,可以正常訪問,與上圖相同,這裡不再貼訪問結果圖。
改動二:
再增加一個實現類Woman.java
package com.example.annotation.service.impl;
import com.example.annotation.service.Human;
import org.springframework.stereotype.Service;
/**
* service介面第二實現類
* @author Administrator
*/
@Service
public class Woman implements Human {
public String runMarathon() {
return "An woman run marathon";
}
}
HumanController.java 註解使用@Resource
@Resource
private Human human;
啟動springboot,控制檯會報錯,報錯資訊太多,擷取關鍵資訊
2018-09-10 16:07:10.362 WARN 5592 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name "humanController": Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type "com.example.annotation.service.Human" available: expected single matching bean but found 2: man,woman
找關鍵資訊 expected single matching bean but found 2: man,woman,被期望的單一結果被匹配到兩個結果man和woman。
這裡,我們需要藉助@Resource註解的name屬性或@Qualifier來確定一個合格的實現類
程式碼修改為
@Resource(name="woman")
private Human human;
或
@Resource
@Qualifier("woman")
private Human human;
上面,我們指定了Human介面的實現類是Woman.java,啟動springboot,訪問 http://localhost:8080/an/run
改動三:
在改動二的基礎上,將註解替換為@Autowired,啟動報錯
Description:
Field human in com.example.annotation.controller.HumanController required a single bean, but 2 were found:
- man: defined in file [D:\DEV_ENV\springbootws\annotation\target\classes\com\example\annotation\service\impl\Man.class]
- woman: defined in file [D:\DEV_ENV\springbootws\annotation\target\classes\com\example\annotation\service\impl\Woman.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
報錯資訊很明顯,HumanController需要一個bean實現,但是找到了兩個 man 和woman
解決方案:使用@Primary註解,在有多個實現bean時告訴spring首先@Primary修飾的那個;或者使用@Qualifier來標註需要注入的類。
@Qualifier修改方式與改動二的相同,依然是修改HumanController.java 中間注入的Human上面,這裡不再複述
@Primary是修飾實現類的,告訴spring,如果有多個實現類時,優先注入被@Primary註解修飾的那個。這裡,我們希望注入Man.java ,那麼修改Man.java為
package com.example.annotation.service.impl;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import com.example.annotation.service.Human;
/**
* service介面第一實現類
* @author Administrator
*/
@Service
@Primary
public class Man implements Human {
public String runMarathon() {
return "A man run marathon";
}
}
啟動springboot後,可以看到注入的已經是Man.java 了。
回覆列表
1、首先我們看下 @Autowired 和 @Resource 的用法
@Autowired 的用法@Autowired 是Spring自帶的註解,位於 包
org.springframework.beans.factory.annotation
下1、自動注入
1、自動裝配可以按照型別進行裝配,如果在IOC容器發現多個相同型別的元件怎麼辦呢?
我們可以按照屬性名稱來進行裝配,比如存在UserDao1 和 UserDao2
@Autowired
private UserDao userDao;
我們透過@Autowied 修飾 屬性名稱時 userDao, 那麼 先用byType來查詢,如果IOC容器中存在多個相同型別的元件,則使用userDao名稱來找,若屬性名稱為userDao2,那麼就載入userDao2的元件。
2、如果我們需要需要指定特定的元件來進行裝配呢?又該怎麼辦呢?
可以使用@Qualifier("userDao") 來指定裝配的元件 或者 在配置類的@Bean上加上
@Primary註解。
@Autowired
@Qualifier("userDao")
private UserDao userDao2;
3、如果我們載入的時候既沒有userDao有沒有userDao2,那麼裝配的時候是什麼現象呢?
配置如下:
上圖初始化了兩個UserDao, Bean元件名稱為 userDao3 和 userDao2。
再次注入的 時候:
先按byType去查詢,查詢的時候發現有兩個UserDao型別,從而又轉換用userDao名稱查詢。但是又查不到,所以 報一下錯誤。
No qualifying bean of type "com.wy.study.autowired.UserDao" available:
expected single matching bean but found 2: userDao3,userDao2
上述的錯誤是,期望去獲取一個例項,但是卻找到了兩個。
如果不想丟擲異常呢,我們需要指定required 為false的時候就可以了
@Autowired(required = false)
private UserDao userDao;
但是上述只能保證 啟動的時候不報錯,但是呼叫的時候還是得報錯。
4、@Autowired 也可以使用在方法引數上
4、@Autowired 標註在構造方法上
@Resource 註解@Resource 是由J2EE 體用的,需要匯入包 javax.annotation.Resource。
功能和@Autowired 的功能差不多一樣,但是不支援@Primary 和 @Qualifier的支援
@Resource 有兩個屬性: name 和 type
name: bean的名稱 type: bean的型別
預設按照 byName自動注入
有下面4中情況
① 如果只是指定了name屬性,則從IOC容器中查詢名稱為userDao的bean進行裝配,找不到則丟擲 異常。
@Resource(name = "userDao")
private UserDao userDao;
② 如果指定了type,則從IOC容器中查詢UserDao型別的Bean,找不到或者找到多個,都會丟擲異常。
④ 若兩個屬性都使用了,則需要找到唯一匹配的bean進行裝配,找不到則丟擲異常。
@Autowired 註解 在Spring原始碼中的解析容器對Bean的自動裝配發生在容器對Bean依賴注入的過程中,在對Spring IOC 容器的以阿里注入原始碼分析,我們已經 知道容器對bean例項物件的依賴屬性注入發生在
AbstractAutowireCapableBeanFactory 類的 populateBean 方法。下面我們對其的解析 進行說明。
1、AbstractAutowireCapableBeanFactory 對Bean例項物件進行屬性依賴注入
當應用程式第一次getBean() 方法時,向IOC容器索取Bean時,容器建立例項物件,並且對Bean例項物件進行屬性依賴注入,AbstractAutowireCapableBeanFactory 的populateBean 方法對屬性進行依賴注入
呼叫棧如下:
原始碼解釋如下:
2、Spring IOC 容器根據Bean名稱或者型別進行autowired 自動屬性依賴注入
Spring IOC容器根據Bean名稱或者型別進行autowired自動屬性依賴注入的程式碼如下:
從上面可以看到,透過屬性名進行自動依賴注入相比透過屬性型別進行自動依賴注入要稍微簡單一些,但是真正實現屬性注入的是 DefaultSingletonBeanRegistry 類的registerDependentBean方法進行注入;
解析步驟如下:
處理bean名稱,將別名轉換為規範的Bean名稱
String canonicalName = canonicalName(beanName);
下面是多執行緒同步,保證容器內的資料的一致性
在容器中透過"Bean名稱找到全部依賴Bean名稱集合“查詢指定名稱Bean的依賴Bean
synchronized (this.dependentBeanMap)
(1) 獲取指定 名稱Bean的所有依賴Bean名稱
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
如果 dependentBeanMap 中麼有,則把依賴的bean放進去
(2) 將bean所依賴的Bean新增到容器的集合中
dependentBeans.add(dependentBeanName);
可以看出,autowired 的實現過程如下:
① 對Bean的屬性呼叫getBean方法,完成依賴Bean的初始化和依賴注入
② 將依賴Bean的屬性引用設定到被依賴的Bean屬性上
總結1、@Resource和@Autowired都可以用來裝配bean,都可以寫在欄位上或者是寫在setter方法上。
2、@Autowired 預設是按照byType進行裝配的,所以預設情況下依賴的物件必須存在,如果允許為null,則可以使用required屬性為false,如果想使用byName進行裝配,那麼可以和@Qualifier註解相互配合。
3、@Resource預設是按照byName進行裝配的,如果指定了name,則會查詢id指定name的bean進行裝配,如果指定了type,則會從Spring IOC 中 找到唯一的Bean進行裝配,找不到則排除異常。
推薦使用@Resource註解在欄位上,這樣就不用寫setter方法了,並且這個註解屬於J2EE的,減少了與Spring的耦合。