小編給大家分享一下Spring中自動裝配和Autowired注解怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
我們提供的服務(wù)有:網(wǎng)站設(shè)計制作、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、新巴爾虎左ssl等。為上1000家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的新巴爾虎左網(wǎng)站制作公司
一、自動裝配
當(dāng)Spring裝配Bean屬性時,有時候非常明確,就是需要將某個Bean的引用裝配給指定屬性。比如,如果我們的應(yīng)用上下文中只有一個org.mybatis.spring.SqlSessionFactoryBean類型的Bean,那么任意一個依賴SqlSessionFactoryBean的其他Bean就是需要這個Bean。畢竟這里只有一個SqlSessionFactoryBean的Bean。
為了應(yīng)對這種明確的裝配場景,Spring提供了自動裝配(autowiring)。與其顯式的裝配Bean屬性,為何不讓Spring識別出可以自動裝配的場景。
當(dāng)涉及到自動裝配Bean的依賴關(guān)系時,Spring有多種處理方式。因此,Spring提供了4種自動裝配策略。
public interface AutowireCapableBeanFactory{ //無需自動裝配 int AUTOWIRE_NO = 0; //按名稱自動裝配bean屬性 int AUTOWIRE_BY_NAME = 1; //按類型自動裝配bean屬性 int AUTOWIRE_BY_TYPE = 2; //按構(gòu)造器自動裝配 int AUTOWIRE_CONSTRUCTOR = 3; //過時方法,Spring3.0之后不再支持 @Deprecated int AUTOWIRE_AUTODETECT = 4; }
Spring在AutowireCapableBeanFactory
接口中定義了這幾種策略。其中,AUTOWIRE_AUTODETECT
被標(biāo)記為過時方法,在Spring3.0之后已經(jīng)不再支持。
1、byName
它的意思是,把與Bean的屬性具有相同名字的其他Bean自動裝配到Bean的對應(yīng)屬性中。聽起來可能比較拗口,我們來看個例子。
首先,在User的Bean中有個屬性Role myRole,再創(chuàng)建一個Role的Bean,它的名字如果叫myRole,那么在User中就可以使用byName來自動裝配。
public class User{ private Role myRole; } public class Role { private String id; private String name; }
上面是Bean的定義,再看配置文件。
如上所述,只要屬性名稱和Bean的名稱可以對應(yīng),那么在user的Bean中就可以使用byName來自動裝配。那么,如果屬性名稱對應(yīng)不上呢?
2、byType
是的,如果不使用屬性名稱來對應(yīng),你也可以選擇使用類型來自動裝配。它的意思是,把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應(yīng)屬性中。
還是上面的例子,如果使用byType,Role Bean的ID都可以省去。
3、constructor
它是說,把與Bean的構(gòu)造器入?yún)⒕哂邢嗤愋偷钠渌鸅ean自動裝配到Bean構(gòu)造器的對應(yīng)入?yún)⒅?。值的注意的是,具有相同類型的其他Bean這句話說明它在查找入?yún)⒌臅r候,還是通過Bean的類型來確定。
構(gòu)造器中入?yún)⒌念愋蜑镽ole
public class User{ private Role role; public User(Role role) { this.role = role; } }
4、autodetect
它首先會嘗試使用constructor進(jìn)行自動裝配,如果失敗再嘗試使用byType。不過,它在Spring3.0之后已經(jīng)被標(biāo)記為@Deprecated。
5、默認(rèn)自動裝配
默認(rèn)情況下,default-autowire屬性被設(shè)置為none,標(biāo)示所有的Bean都不使用自動裝配,除非Bean上配置了autowire屬性。
如果你需要為所有的Bean配置相同的autowire屬性,有個辦法可以簡化這一操作。
在根元素Beans上增加屬性default-autowire="byType"。
Spring自動裝配的優(yōu)點不言而喻。但是事實上,在Spring XML配置文件里的自動裝配并不推薦使用,其中筆者認(rèn)為最大的缺點在于不確定性?;蛘叱悄銓φ麄€Spring應(yīng)用中的所有Bean的情況了如指掌,不然隨著Bean的增多和關(guān)系復(fù)雜度的上升,情況可能會很糟糕
二、Autowired
從Spring2.5開始,開始支持使用注解來自動裝配Bean的屬性。它允許更細(xì)粒度的自動裝配,我們可以選擇性的標(biāo)注某一個屬性來對其應(yīng)用自動裝配。
Spring支持幾種不同的應(yīng)用于自動裝配的注解。
Spring自帶的@Autowired注解。
JSR-330的@Inject注解。
JSR-250的@Resource注解。
我們今天只重點關(guān)注Autowired注解,關(guān)于它的解析和注入過程,請參考筆者Spring源碼系列的文章。Spring源碼分析(二)bean的實例化和IOC依賴注入
使用@Autowired很簡單,在需要注入的屬性加入注解即可。
@Autowired UserService userService;
不過,使用它有幾個點需要注意。
1、強(qiáng)制性
默認(rèn)情況下,它具有強(qiáng)制契約特性,其所標(biāo)注的屬性必須是可裝配的。如果沒有Bean可以裝配到Autowired所標(biāo)注的屬性或參數(shù)中,那么你會看到NoSuchBeanDefinitionException
的異常信息。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, SetautowiredBeanNames, TypeConverter typeConverter) throws BeansException { //查找Bean Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); //如果拿到的Bean集合為空,且isRequired,就拋出異常。 if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(type, "", descriptor); } return null; } }
看到上面的源碼,我們可以得到這一信息,Bean集合為空不要緊,關(guān)鍵isRequired條件不能成立,那么,如果我們不確定屬性是否可以裝配,可以這樣來使用Autowired。
@Autowired(required=false) UserService userService;
2、裝配策略
我記得曾經(jīng)有個面試題是這樣問的:Autowired是按照什么策略來自動裝配的呢?
關(guān)于這個問題,不能一概而論,你不能簡單的說按照類型或者按照名稱。但可以確定的一點的是,它默認(rèn)是按照類型來自動裝配的,即byType。
默認(rèn)按照類型裝配
關(guān)鍵點findAutowireCandidates這個方法。
protected MapfindAutowireCandidates( String beanName, Class> requiredType, DependencyDescriptor descriptor) { //獲取給定類型的所有bean名稱,里面實際循環(huán)所有的beanName,獲取它的實例 //再通過isTypeMatch方法來確定 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map result = new LinkedHashMap (candidateNames.length); //根據(jù)返回的beanName,獲取其實例返回 for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { result.put(candidateName, getBean(candidateName)); } } return result; }
按照名稱裝配
可以看到它返回的是一個列表,那么就表明,按照類型匹配可能會查詢到多個實例。到底應(yīng)該裝配哪個實例呢?我看有的文章里說,可以加注解以此規(guī)避。比如@qulifier、@Primary等,實際還有個簡單的辦法。
比如,按照UserService接口類型來裝配它的實現(xiàn)類。UserService接口有多個實現(xiàn)類,分為UserServiceImpl、UserServiceImpl2。那么我們在注入的時候,就可以把屬性名稱定義為Bean實現(xiàn)類的名稱。
@Autowired UserService UserServiceImpl2;
這樣的話,Spring會按照byName來進(jìn)行裝配。首先,如果查到類型的多個實例,Spring已經(jīng)做了判斷。
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, SetautowiredBeanNames, TypeConverter typeConverter) throws BeansException { //按照類型查找Bean實例 Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); //如果Bean集合為空,且isRequired成立就拋出異常 if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(type, "", descriptor); } return null; } //如果查找的Bean實例大于1個 if (matchingBeans.size() > 1) { //找到最合適的那個,如果沒有合適的。。也拋出異常 String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } if (autowiredBeanNames != null) { autowiredBeanNames.add(primaryBeanName); } return matchingBeans.get(primaryBeanName); } }
可以看出,如果查到多個實例,determineAutowireCandidate
方法就是關(guān)鍵。它來確定一個合適的Bean返回。其中一部分就是按照Bean的名稱來匹配。
protected String determineAutowireCandidate(MapcandidateBeans, DependencyDescriptor descriptor) { //循環(huán)拿到的Bean集合 for (Map.Entry entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); //通過matchesBeanName方法來確定bean集合中的名稱是否與屬性的名稱相同 if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) { return candidateBeanName; } } return null; }
最后我們回到問題上,得到的答案就是:@Autowired默認(rèn)使用byType來裝配屬性,如果匹配到類型的多個實例,再通過byName來確定Bean。
3、主和優(yōu)先級
上面我們已經(jīng)看到了,通過byType可能會找到多個實例的Bean。然后再通過byName來確定一個合適的Bean,如果通過名稱也確定不了呢?
還是determineAutowireCandidate
這個方法,它還有兩種方式來確定。
protected String determineAutowireCandidate(MapcandidateBeans, DependencyDescriptor descriptor) { Class> requiredType = descriptor.getDependencyType(); //通過@Primary注解來標(biāo)識Bean String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType); if (primaryCandidate != null) { return primaryCandidate; } //通過@Priority(value = 0)注解來標(biāo)識Bean value為優(yōu)先級大小 String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType); if (priorityCandidate != null) { return priorityCandidate; } return null; }
Primary
它的作用是看Bean上是否包含@Primary注解,如果包含就返回。當(dāng)然了,你不能把多個Bean都設(shè)置為@Primary,不然你會得到NoUniqueBeanDefinitionException
這個異常。
protected String determinePrimaryCandidate(MapcandidateBeans, Class> requiredType) { String primaryBeanName = null; for (Map.Entry entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); if (isPrimary(candidateBeanName, beanInstance)) { if (primaryBeanName != null) { boolean candidateLocal = containsBeanDefinition(candidateBeanName); boolean primaryLocal = containsBeanDefinition(primaryBeanName); if (candidateLocal && primaryLocal) { throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(), "more than one 'primary' bean found among candidates: " + candidateBeans.keySet()); } else if (candidateLocal) { primaryBeanName = candidateBeanName; } } else { primaryBeanName = candidateBeanName; } } } return primaryBeanName; }
Priority
你也可以在Bean上配置@Priority注解,它有個int類型的屬性value,可以配置優(yōu)先級大小。數(shù)字越小的,就被優(yōu)先匹配。同樣的,你也不能把多個Bean的優(yōu)先級配置成相同大小的數(shù)值,否則NoUniqueBeanDefinitionException
異常照樣出來找你。
protected String determineHighestPriorityCandidate(MapcandidateBeans, Class> requiredType) { String highestPriorityBeanName = null; Integer highestPriority = null; for (Map.Entry entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); Integer candidatePriority = getPriority(beanInstance); if (candidatePriority != null) { if (highestPriorityBeanName != null) { //如果優(yōu)先級大小相同 if (candidatePriority.equals(highestPriority)) { throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(), "Multiple beans found with the same priority ('" + highestPriority + "') " + "among candidates: " + candidateBeans.keySet()); } else if (candidatePriority < highestPriority) { highestPriorityBeanName = candidateBeanName; highestPriority = candidatePriority; } } else { highestPriorityBeanName = candidateBeanName; highestPriority = candidatePriority; } } } return highestPriorityBeanName; }
最后,有一點需要注意。Priority的包在javax.annotation.Priority;,如果想使用它還要引入一個坐標(biāo)。
javax.annotation javax.annotation-api 1.2
以上是“Spring中自動裝配和Autowired注解怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!