Spring中@Autowired注解,@Resource注解和xml default-autowire區別


[java]  view plain  copy
 
  1. String resourceName = resource.name();  
  2. this.isDefaultName = !StringUtils.hasLength(resourceName);  
  3. if (this.isDefaultName) {  
  4.     resourceName = this.member.getName();  
  5.     if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {  
  6.         resourceName = Introspector.decapitalize(resourceName.substring(3));  
  7.     }  
  8. }  

 

 

[java]  view plain  copy
 
  1. private TxInterface tx2;  
  2.    
  3. public TxInterface getTx3() {  
  4.     return tx2;  
  5. }  
  6.    
  7. public void setTx5(TxInterface tx3) {  
  8.     this.tx2 = tx3;  
  9. }  


Spring不但支持自己定義的@Autowired注解,還支持幾個由JSR-250規范定義的注解,它們分別是@Resource、@PostConstruct以及@PreDestroy。
  @Resource的作用相當於@Autowired,只不過@Autowired按byType自動注入,而@Resource默認按 byName自動注入罷了。@Resource有兩個屬性是比較重要的,分是name和type,Spring將@Resource注解的name屬性解析為bean的名字,而type屬性則解析為bean的類型。所以如果使用name屬性,則使用byName的自動注入策略,而使用type屬性時則使用byType自動注入策略。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略。
  @Resource裝配順序
  1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
  2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
  3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
  4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退為一個原始類型進行匹配,如果匹配則自動裝配;

 

@Autowired 與@Resource的區別:

 

1、 @Autowired與@Resource都可以用來裝配bean. 都可以寫在字段上,或寫在setter方法上。

2、 @Autowired默認按類型裝配(這個注解是屬業spring的),默認情況下必須要求依賴對象必須存在,如果要允許null值,可以設置它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier注解進行使用,如下:

[java]  view plain  copy
 
  1. @Autowired() @Qualifier("baseDao")  
  2. private BaseDao baseDao;  

3、@Resource(這個注解屬於J2EE的),默認安裝名稱進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,當注解寫在字段上時,默認取字段名進行安裝名稱查找,如果注解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。

 

[java]  view plain  copy
 
  1. @Resource(name="baseDao")  
  2. private BaseDao baseDao;  

處理類和處理順序異同
從spring 2.5開始,spring又提供了一個Autowired以及javaEE中標准的Resource注釋,都好像可以實現類似的自動注入。那么是不是每個都實現同樣的方式呢,這里面的幾個配置到底有哪些異同點。哪個更全,哪個更優先,這些都需要對spring的內部原理有詳細的了解才可以進行了解。
在以下文章時,首先有幾個概念需要列出:
字段名稱:即fieldName,這個即propertyDescriper的getPropertyName返回信息。
setter名稱:即方法setter除set之外的名稱,如setAbc,則名稱為abc,這里的abc不一定和fieldName相同。
參數名稱:即在參數中所定義的參數的名稱,如setAbc(Abc a123)。這里的參數名稱就是a123。
本文所使用spring版本為spring3.0.2。

 

default-autowire是在xml中進行配置的,而這個配置從spring初始就提供了。而Autowired注解,則是從2.5自支持以java1.5之后才出現的,這就必然導致對相應的處理以及邏輯是不同的。那么每個方式的處理順序是怎樣的呢,從我寫的文章:Spring中獲取一個bean的流程-2.也可以由下面的代碼得出:

[java]  view plain  copy
 
  1. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||  
  2.                 mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {  
  3.             MutablePropertyValues newPvs = new MutablePropertyValues(pvs);  
  4.    
  5.             // Add property values based on autowire by name if applicable.  
  6.             if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {  
  7.                 autowireByName(beanName, mbd, bw, newPvs);  
  8.             }  
  9.    
  10.             // Add property values based on autowire by type if applicable.  
  11.             if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {  
  12.                 autowireByType(beanName, mbd, bw, newPvs);  
  13.             }  
  14.    
  15.             pvs = newPvs;  
  16.         }  
  17. ......  
  18.             for (BeanPostProcessor bp : getBeanPostProcessors()) {  
  19.                 if (bp instanceof InstantiationAwareBeanPostProcessor) {  
  20.                     InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;  
  21.                     pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);  

以上代碼來源於類AbstractAutowireCapableBeanFactory的populateBean方法。從上可以看出,spring首先處理在bean定義上的autowire屬性,然后再處理后面的InstantiationAwareBeanPostProcessor類。首先bean定義上的autowire屬性,可以來自於<bean>定義時autowire屬性,也可以來自於整個xml定義中<beans>節點中的default-autowire屬性。

那么@Autowired注解和@Resource注解在哪兒處理呢,看上面的代碼,有個InstantiationAwareBeanPostProcessor類,如果你仔細查看里面的實現,你可以發現里面即為處理相應注解類的實現。而這些注解類,只要在xml中啟用了<context:annotation-config/>,即可以開啟這些類了。而我們的Autowired注解,由AutowiredAnnotationBeanPostProcessor來進行處理,而Resource類,則由CommonAnnotationBeanPostProcessor進行處理。

 

處理內容和處理范圍異同

xml中default-autowire配置

首先,針對於xml配置中的default-autowire配置,我們都知道byName是通過name注入而byType是通過類型注入。byType沒有好爭議的,是根據類型從所有bean查找滿足條件的bean,如果找到一個,則使用此bean。但是如果沒有找到,則不會報錯,但是如果發現有2個以上的侯選者,則會報No unique bean of type的錯誤信息。

針對於byName,則是根據propertyDescriptor,即滿足bean規范的字段信息進行注入。之所以這里重點提bean規范,請看以下代碼:

 

這里是不會進行任何注入的,因為里面的tx2,根本不滿足bean規范。但如果將方法setTx5修改為setTx2,則就滿足bean規范,就會進行byName注入了。

@Autowired注解

再來看Autowired注解,有的人說autowired注解是按照byType方式進行配置,其實這個說法是錯的,至少是不完善的。為什么呢,這需要我們來查看整個autowire的流程,如以下代碼示:

[java]  view plain  copy
 
  1. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);  
  2. if (matchingBeans.isEmpty()) {  
  3.     if (descriptor.isRequired()) {  
  4.         raiseNoSuchBeanDefinitionException(type, "", descriptor);  
  5.     }  
  6.     return null;  
  7. }  
  8. if (matchingBeans.size() > 1) {  
  9.     String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);  
  10.     if (primaryBeanName == null) {  
  11.         throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +  
  12.                 matchingBeans.size() + ": " + matchingBeans.keySet());  
  13.     }  
  14.     if (autowiredBeanNames != null) {  
  15.         autowiredBeanNames.add(primaryBeanName);  
  16.     }  
  17.     return matchingBeans.get(primaryBeanName);  
  18. }  
  19. // We have exactly one match.  
  20. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();  
  21. if (autowiredBeanNames != null) {  
  22.     autowiredBeanNames.add(entry.getKey());  
  23. }  
  24. return entry.getValue();  

以上代碼來自於類DefaultListableBeanFactory的doResolveDependency方法。這是由類AutowiredAnnotationBeanPostProcessor類通過調用inject方法時,需要通過調用beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter)來解析引用信息。我們仔細看以上的邏輯,可以從下面的順序進行處理。

  1. 首先根據類型找到所有可以滿足條件的bean
  2. 判斷bean長度,如果沒有,則根據@autowired中的required屬性進行判斷是否拋出異常(默認為true)
  3. 如果多於一個,則嘗試尋找最優的那一個,如果最優的未找到,則拋出異常
  4. 如果只有一個,則直接使用此bean

這個邏輯與byType類似,但還不完全相同。不相同的則在第3點處理上,byType如果多於一個,則直接拋出異常。而這里有一個尋找最優bean的過程。即方法determinePrimaryCandidate的實現。實現代碼不再列出。但根據@Autowired所放置位置有所不同。

放置在setter方法上,則使用MethodParameter的parameterName進行查找,請注意這里的parameterName。這個屬性只有在編譯時保留了debug的localVariable才會存在,否則即為null屬性。這個屬性即參數的名稱。如果localVariable不存在,則直接退化為byType。如果有,就按照參數名稱進行查找。這里的參數名稱不是setter后面的名稱,也不是字段名。如以下代碼所示:

1
public  void  setTx2(TxInterface tx1) { this .tx2 = tx1;}

這里的名稱為tx1,而不是tx2。

放置在字段上,則直接使用字段名稱。進行查找。

@Resource注解

最后看Resource注解,也有的人說resource是按byName注解,即就完全錯了。實際上Resource根本不會走byName方式,我們來看@Resource如何尋找一個bean。默認在不寫Resource(name)的情況下:

[java]  view plain  copy
 
  1. String name = element.name;  
  2.    
  3. if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&  
  4.         factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {  
  5.     autowiredBeanNames = new LinkedHashSet<String>();  
  6.     resource = ((AutowireCapableBeanFactory) factory).resolveDependency(  
  7.             element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames,null);  
  8. }  
  9. else {  
  10.     resource = factory.getBean(name, element.lookupType);  
  11.     autowiredBeanNames = Collections.singleton(name);  
  12. }  
  13.    
  14. if (factory instanceof ConfigurableBeanFactory) {  
  15.     ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;  
  16.     for (String autowiredBeanName : autowiredBeanNames) {  
  17.         beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);  
  18.     }  
  19. }  
  20.    
  21. return resource;  

以上代碼來自於類CommonAnnotationBeanPostProcessor中的autowireResource方法,是由此類通過getResourceToInject獲取將要注入的bean來調用的。上面的方法詳細描述了整個過程,如下所示:

  1. 獲取element的名稱,判斷beanFactory是否存在此name的bean
  2. 如果存在,則直接使用此name進行查詢
  3. 否則退化到默認的autowire查找方式

從上面的第三步,可以看出,Resource在沒有根據name查找到的情況下,會走Autowire的方式。所以,從范圍來看Resouce的查找范圍比Autowire范圍更大。

再來看第1步,獲取element的名稱,這里說是element的名稱,因為它的來源有2個地方。一是在resouce注解中配置的name屬性,第二就是setter名稱或者是field名稱(取決於@Resource的配置地點),。這里說的是setter名稱,而不是屬性名稱,這就是需要注意的地方。來源代碼如下所示:

 

來源於類ResourceElement的initAnnotation方法。因此,如果方法為如下所示:

[java]  view plain  copy
 
  1. @Resource  
  2. public void setTx2(TxInterface tx5) {  
  3.     this.tx4 = tx5;  
  4. }  

則獲取到的name就是tx2,而不是字段名稱tx4。當然,上面的寫法不是標准的java bean規范寫法,但只是演示這種情況。那么,在系統存在多個滿足type的情況下,如果上面的方法中的tx2的bean未找到,那么接下來就尋找名為tx5(autowire規則),再找不到就該報Not Unique異常了。

值得注意的是,如果在使用resource時,根據resource的name找到了bean,但該bean並不是所需要的bean類型,則就要報類型不匹配錯誤了。即spring在查找時,並沒有保證類型判斷,即你配置一個name的tx2的bean,但該類型即為TxInterface2而不是TxInterface,則spring在后期直接報異常,而不會fallback了。但Autowired注解則不會產生這種情況,因為它只會從滿足type的情況中的bean中查找。

總結

在使用Autowired注解和Resource注解以及xml中的default-autowire注解時,需要詳細地了解每個注解的工作方式和工作范圍,在大多數情況下。這幾種方式都差不多,但在細節方面有差異。從筆者對於代碼的嚴謹角度,我並不推薦在xml中配置default-autowire,因為這會導致所有的bean,不管需不需要注入,spring都會幫你注入。從一方面是好事,從另一方面就管得太多。如果確實要配置default-autowire,請再配置另一個屬性default-autowire-candidates,這個屬性可以固定default-autowire的范圍,比如*Service,可以只針對Service結尾的bean進行autowire包裝。

最后,@Autowire注解不是xml配置中的default-autowire-byType,而@Resource也不是@Autowire,更不是xml配置中的default-autowire-byName。不能夠簡單地混為一談。

注意
1) ,如果沒有指定name屬性,並且按照默認的名稱仍然找不到依賴對象時, @Resource注解會回退到按類型裝配。但一旦指定了name屬性,就只能按名稱裝配了。推薦使用:@Resource注解在字段上,這樣就不用寫setter方法了,並且這個注解是屬於J2EE的,減少了與spring的耦合。
2) , Hibernate JPA注解一般加在屬性或者屬性getter方法上,Spring注解一般加在屬性或者屬性setter方法上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM