亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

徹底搞明白Spring中的自動裝配和Autowired注解的使用

2024-07-14 08:43:39
字體:
來源:轉載
供稿:網友

一、自動裝配

當Spring裝配Bean屬性時,有時候非常明確,就是需要將某個Bean的引用裝配給指定屬性。比如,如果我們的應用上下文中只有一個org.mybatis.spring.SqlSessionFactoryBean類型的Bean,那么任意一個依賴SqlSessionFactoryBean的其他Bean就是需要這個Bean。畢竟這里只有一個SqlSessionFactoryBean的Bean。

為了應對這種明確的裝配場景,Spring提供了自動裝配(autowiring)。與其顯式的裝配Bean屬性,為何不讓Spring識別出可以自動裝配的場景。

當涉及到自動裝配Bean的依賴關系時,Spring有多種處理方式。因此,Spring提供了4種自動裝配策略。

public interface AutowireCapableBeanFactory{ //無需自動裝配 int AUTOWIRE_NO = 0; //按名稱自動裝配bean屬性 int AUTOWIRE_BY_NAME = 1; //按類型自動裝配bean屬性 int AUTOWIRE_BY_TYPE = 2; //按構造器自動裝配 int AUTOWIRE_CONSTRUCTOR = 3; //過時方法,Spring3.0之后不再支持 @Deprecated int AUTOWIRE_AUTODETECT = 4;}

Spring在AutowireCapableBeanFactory接口中定義了這幾種策略。其中,AUTOWIRE_AUTODETECT被標記為過時方法,在Spring3.0之后已經不再支持。

1、byName

它的意思是,把與Bean的屬性具有相同名字的其他Bean自動裝配到Bean的對應屬性中。聽起來可能比較拗口,我們來看個例子。

首先,在User的Bean中有個屬性Role myRole,再創建一個Role的Bean,它的名字如果叫myRole,那么在User中就可以使用byName來自動裝配。

public class User{ private Role myRole;}public class Role { private String id;  private String name;}

上面是Bean的定義,再看配置文件。

<bean id="myRole" class="com.viewscenes.netsupervisor.entity.Role"> <property name="id" value="1001"></property> <property name="name" value="管理員"></property></bean><bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byName"></bean>

如上所述,只要屬性名稱和Bean的名稱可以對應,那么在user的Bean中就可以使用byName來自動裝配。那么,如果屬性名稱對應不上呢?

2、byType

是的,如果不使用屬性名稱來對應,你也可以選擇使用類型來自動裝配。它的意思是,把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。

<bean class="com.viewscenes.netsupervisor.entity.Role"> <property name="id" value="1001"></property> <property name="name" value="管理員"></property></bean><bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="byType"></bean>

還是上面的例子,如果使用byType,Role Bean的ID都可以省去。

3、constructor

它是說,把與Bean的構造器入參具有相同類型的其他Bean自動裝配到Bean構造器的對應入參中。值的注意的是,具有相同類型的其他Bean這句話說明它在查找入參的時候,還是通過Bean的類型來確定。
構造器中入參的類型為Role

public class User{ private Role role; public User(Role role) { this.role = role; }}<bean id="user" class="com.viewscenes.netsupervisor.entity.User" autowire="constructor"></bean>

4、autodetect

它首先會嘗試使用constructor進行自動裝配,如果失敗再嘗試使用byType。不過,它在Spring3.0之后已經被標記為@Deprecated。

5、默認自動裝配

默認情況下,default-autowire屬性被設置為none,標示所有的Bean都不使用自動裝配,除非Bean上配置了autowire屬性。
如果你需要為所有的Bean配置相同的autowire屬性,有個辦法可以簡化這一操作。

在根元素Beans上增加屬性default-autowire="byType"。

<beans default-autowire="byType">

Spring自動裝配的優點不言而喻。但是事實上,在Spring XML配置文件里的自動裝配并不推薦使用,其中筆者認為最大的缺點在于不確定性?;蛘叱悄銓φ麄€Spring應用中的所有Bean的情況了如指掌,不然隨著Bean的增多和關系復雜度的上升,情況可能會很糟糕

二、Autowired

從Spring2.5開始,開始支持使用注解來自動裝配Bean的屬性。它允許更細粒度的自動裝配,我們可以選擇性的標注某一個屬性來對其應用自動裝配。

Spring支持幾種不同的應用于自動裝配的注解。

  • Spring自帶的@Autowired注解。
  • JSR-330的@Inject注解。
  • JSR-250的@Resource注解。

我們今天只重點關注Autowired注解,關于它的解析和注入過程,請參考筆者Spring源碼系列的文章。Spring源碼分析(二)bean的實例化和IOC依賴注入

使用@Autowired很簡單,在需要注入的屬性加入注解即可。

@AutowiredUserService userService;

不過,使用它有幾個點需要注意。

1、強制性

默認情況下,它具有強制契約特性,其所標注的屬性必須是可裝配的。如果沒有Bean可以裝配到Autowired所標注的屬性或參數中,那么你會看到NoSuchBeanDefinitionException的異常信息。

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,  Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {  //查找Bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); //如果拿到的Bean集合為空,且isRequired,就拋出異常。 if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) {  raiseNoSuchBeanDefinitionException(type, "", descriptor); } return null; }}

看到上面的源碼,我們可以得到這一信息,Bean集合為空不要緊,關鍵isRequired條件不能成立,那么,如果我們不確定屬性是否可以裝配,可以這樣來使用Autowired。

@Autowired(required=false)UserService userService;

2、裝配策略

我記得曾經有個面試題是這樣問的:Autowired是按照什么策略來自動裝配的呢?
關于這個問題,不能一概而論,你不能簡單的說按照類型或者按照名稱。但可以確定的一點的是,它默認是按照類型來自動裝配的,即byType。

默認按照類型裝配

關鍵點findAutowireCandidates這個方法。

protected Map<String, Object> findAutowireCandidates( String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {  //獲取給定類型的所有bean名稱,里面實際循環所有的beanName,獲取它的實例 //再通過isTypeMatch方法來確定 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(  this, requiredType, true, descriptor.isEager());   Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);  //根據返回的beanName,獲取其實例返回 for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) {  result.put(candidateName, getBean(candidateName)); } } return result;}

按照名稱裝配

可以看到它返回的是一個列表,那么就表明,按照類型匹配可能會查詢到多個實例。到底應該裝配哪個實例呢?我看有的文章里說,可以加注解以此規避。比如@qulifier、@Primary等,實際還有個簡單的辦法。

比如,按照UserService接口類型來裝配它的實現類。UserService接口有多個實現類,分為UserServiceImpl、UserServiceImpl2。那么我們在注入的時候,就可以把屬性名稱定義為Bean實現類的名稱。

@AutowiredUserService UserServiceImpl2;

這樣的話,Spring會按照byName來進行裝配。首先,如果查到類型的多個實例,Spring已經做了判斷。

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,  Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {   //按照類型查找Bean實例 Map<String, Object> 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方法就是關鍵。它來確定一個合適的Bean返回。其中一部分就是按照Bean的名稱來匹配。

protected String determineAutowireCandidate(Map<String, Object> candidateBeans,   DependencyDescriptor descriptor) { //循環拿到的Bean集合 for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); //通過matchesBeanName方法來確定bean集合中的名稱是否與屬性的名稱相同 if (matchesBeanName(candidateBeanName, descriptor.getDependencyName())) {  return candidateBeanName; } } return null;}

最后我們回到問題上,得到的答案就是:@Autowired默認使用byType來裝配屬性,如果匹配到類型的多個實例,再通過byName來確定Bean。

3、主和優先級

上面我們已經看到了,通過byType可能會找到多個實例的Bean。然后再通過byName來確定一個合適的Bean,如果通過名稱也確定不了呢?

還是determineAutowireCandidate這個方法,它還有兩種方式來確定。

protected String determineAutowireCandidate(Map<String, Object> candidateBeans,   DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); //通過@Primary注解來標識Bean String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType); if (primaryCandidate != null) { return primaryCandidate; } //通過@Priority(value = 0)注解來標識Bean value為優先級大小 String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType); if (priorityCandidate != null) { return priorityCandidate; } return null;}

Primary

它的作用是看Bean上是否包含@Primary注解,如果包含就返回。當然了,你不能把多個Bean都設置為@Primary,不然你會得到NoUniqueBeanDefinitionException這個異常。

protected String determinePrimaryCandidate(Map<String, Object> candidateBeans, Class<?> requiredType) { String primaryBeanName = null; for (Map.Entry<String, Object> 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,可以配置優先級大小。數字越小的,就被優先匹配。同樣的,你也不能把多個Bean的優先級配置成相同大小的數值,否則NoUniqueBeanDefinitionException異常照樣出來找你。

protected String determineHighestPriorityCandidate(Map<String, Object> candidateBeans,      Class<?> requiredType) { String highestPriorityBeanName = null; Integer highestPriority = null; for (Map.Entry<String, Object> entry : candidateBeans.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); Integer candidatePriority = getPriority(beanInstance); if (candidatePriority != null) {  if (highestPriorityBeanName != null) {  //如果優先級大小相同  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;,如果想使用它還要引入一個坐標。

<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.2</version></dependency>

三、總結

本章節重點闡述了Spring中的自動裝配的幾種策略,又通過源碼分析了Autowired注解的使用方式。

在Spring3.0之后,有效的自動裝配策略分為byType、byName、constructor三種方式。注解Autowired默認使用byType來自動裝配,如果存在類型的多個實例就嘗試使用byName匹配,如果通過byName也確定不了,可以通過Primary和Priority注解來確定。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
69视频在线免费观看| 亚洲一区二区日本| 欧美在线免费观看| 欧美精品18videos性欧美| 青青a在线精品免费观看| 久色乳综合思思在线视频| 国产精品久久激情| 欧美在线性视频| 亚洲伊人一本大道中文字幕| 日韩中文视频免费在线观看| 日本成熟性欧美| 亚洲免费小视频| 国产视频丨精品|在线观看| 亚洲欧洲偷拍精品| 欧美日韩中文字幕综合视频| 国产成人精品在线观看| 国产香蕉精品视频一区二区三区| 国产精品自拍网| 久久韩国免费视频| 日韩av在线网页| 亚洲第一视频网站| 国产91精品久久久久久| 国产精品久久久久久超碰| 亚洲欧美综合v| 国产精品扒开腿做| 日韩hd视频在线观看| 精品中文视频在线| 日韩精品免费电影| 少妇精69xxtheporn| 97国产精品免费视频| 日韩成人高清在线| 国产美女主播一区| 国产欧美日韩精品丝袜高跟鞋| 欧美午夜精品伦理| 日韩中文字幕国产| 欧美日韩一区二区三区| 国产精品扒开腿做| 91网在线免费观看| 精品无人国产偷自产在线| 久久免费福利视频| 精品国产户外野外| 国产亚洲福利一区| 成人在线中文字幕| 九九热99久久久国产盗摄| 日韩成人xxxx| 8090成年在线看片午夜| 国产精品久久久av| 日韩免费在线视频| 国产成人精品电影久久久| 7777免费精品视频| 亚洲国产精品成人一区二区| 亚洲欧美三级在线| 91成人性视频| 777午夜精品福利在线观看| 欧美视频在线观看 亚洲欧| 欧美日韩亚洲一区二区| 欧美激情精品久久久久久变态| 午夜精品一区二区三区视频免费看| 欧美巨猛xxxx猛交黑人97人| 久久在线精品视频| 日韩av一卡二卡| 国产成人拍精品视频午夜网站| 成人免费在线网址| 成人春色激情网| 亚洲第一区中文字幕| 国产精品va在线播放我和闺蜜| 日韩精品极品视频免费观看| 中文字幕亚洲综合久久| 亚洲高清一区二| 国产精品久久一| 91美女福利视频高清| 国产精品偷伦视频免费观看国产| 国产精品视频999| 亚洲高清久久久久久| 日本午夜精品理论片a级appf发布| 美乳少妇欧美精品| 色99之美女主播在线视频| 国产精品av网站| 国产精品视频自在线| 国产激情999| 国产一区二区三区视频免费| 亚洲第一男人av| www.精品av.com| 国产精品十八以下禁看| 成人免费视频97| 北条麻妃一区二区在线观看| 日韩在线激情视频| 久久人人爽亚洲精品天堂| 亚洲欧美国产高清va在线播| 久久久精品在线观看| 色综合伊人色综合网| 国产成人精品a视频一区www| 欧美黑人一区二区三区| 欧美激情xxxxx| 国产v综合ⅴ日韩v欧美大片| 亚洲美腿欧美激情另类| 亚洲天堂av高清| 亚洲人成自拍网站| 91老司机精品视频| 亚洲精品美女久久久| 成人黄色影片在线| 亚洲精品狠狠操| 精品一区精品二区| 亚洲视频在线播放| 51色欧美片视频在线观看| 中文字幕亚洲综合久久| 亚洲新中文字幕| 成人中心免费视频| 欧美精品国产精品日韩精品| 欧美与欧洲交xxxx免费观看| 91亚洲va在线va天堂va国| 欧美激情第三页| 福利精品视频在线| www.欧美免费| 欧美一区二区影院| 搡老女人一区二区三区视频tv| 欧美日韩国产中文字幕| 色www亚洲国产张柏芝| 91色精品视频在线| 日韩有码在线电影| 久久九九国产精品怡红院| 狠狠久久亚洲欧美专区| 亚洲2020天天堂在线观看| 国产精品久久久久久久久久99| 欧美裸身视频免费观看| 国自产精品手机在线观看视频| 欧美成人亚洲成人日韩成人| 秋霞午夜一区二区| 色妞色视频一区二区三区四区| 欧美一区二粉嫩精品国产一线天| 欧美久久精品一级黑人c片| 中文字幕v亚洲ⅴv天堂| 成人高清视频观看www| 国产精品久久av| 97成人精品区在线播放| 国产精品日韩在线观看| 91精品国产高清自在线看超| 久久久亚洲天堂| 国产精品久久中文| 国产成人精品电影| 国产精品极品美女粉嫩高清在线| 亚洲人成电影在线观看天堂色| 欧美国产乱视频| 久久久久久久国产精品| 久久国产精品首页| 麻豆国产va免费精品高清在线| 欧美在线视频免费观看| 亚洲自拍在线观看| 久久亚洲精品一区| 欧美伦理91i| 日日噜噜噜夜夜爽亚洲精品| 91国语精品自产拍在线观看性色| 亚洲欧美国产一本综合首页| 国产精品视频永久免费播放| 亚洲国产精品99久久| 国产成人精品综合久久久| 欧美一级视频免费在线观看| 精品久久久久久国产| 国产精品久久久久久亚洲调教| 亚洲高清在线观看| 日本免费久久高清视频| 亚洲精品mp4| 欧美大片在线看免费观看| 欧美一区二区三区免费视|