在路上

 找回密码
 立即注册
在路上 站点首页 学习 查看内容

详解Java Spring各种依赖注入注解的区别

2016-8-29 13:19| 发布者: zhangjf| 查看: 738| 评论: 0

摘要: 注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired、Resource、Qualifier、Service、Controller、Repository、Component。 Autowired是自动注入,自动从spring的上下文找到合适的bean ...

注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired、Resource、Qualifier、Service、Controller、Repository、Component。

Autowired是自动注入,自动从spring的上下文找到合适的bean来注入

Resource用来指定名称注入

Qualifier和Autowired配合使用,指定bean的名称
Service,Controller,Repository分别标记类是Service层类,Controller层类,数据存储层的类,spring扫描注解配置时,会标记这些类要生成bean。

Component是一种泛指,标记类是组件,spring扫描注解配置时,会标记这些类要生成bean。

Spring对于Bean的依赖注入,支持多种注解方式:

  1. @Resource
  2. javax.annotation
  3. JSR250 (Common Annotations for Java)
  4. @Inject
  5. javax.inject
  6. JSR330 (Dependency Injection for Java)
  7. @Autowired
  8. org.springframework.bean.factory
  9. Spring
复制代码

直观上看起来,@Autowired是Spring提供的注解,其他几个都是JDK本身内建的注解,Spring对这些注解也进行了支持。但是使用起来这三者到底有什么区别呢?笔者经过方法的测试,发现一些有意思的特性。

区别总结如下:

一、@Autowired有个required属性,可以配置为false,这种情况下如果没有找到对应的bean是不会抛异常的。@Inject和@Resource没有提供对应的配置,所以必须找到否则会抛异常。

二、 @Autowired和@Inject基本是一样的,因为两者都是使用AutowiredAnnotationBeanPostProcessor来处理 依赖注入。但是@Resource是个例外,它使用的是CommonAnnotationBeanPostProcessor来处理依赖注入。当然,两者 都是BeanPostProcessor。

  1. @Autowired和@Inject
  2. - 默认 autowired by type
  3. - 可以 通过@Qualifier 显式指定 autowired by qualifier name。
  4. - 如果 autowired by type 失败(找不到或者找到多个实现),则退化为autowired by field name
  5. @Resource
  6. - 默认 autowired by field name
  7. - 如果 autowired by field name失败,会退化为 autowired by type
  8. - 可以 通过@Qualifier 显式指定 autowired by qualifier name
  9. - 如果 autowired by qualifier name失败,会退化为 autowired by field name。但是这时候如果 autowired by field name失败,就不会再退化为autowired by type了。
复制代码

TIPS Qualified name VS Bean name

在Spring设计中,Qualified name并不等同于Bean name,后者必须是唯一的,但是前者类似于tag或者group的作用,对特定的bean进行分类。可以达到getByTag(group)的效果。对 于XML配置的bean,可以通过id属性指定bean name(如果没有指定,默认使用类名首字母小写),通过标签指定qualifier name:

  1. <bean id="lamborghini" class="me.arganzheng.study.spring.autowired.Lamborghini">
  2. <qualifier value="luxury"/>
  3. <!-- inject any dependencies required by this bean -->
  4. </bean>
复制代码

如果是通过注解方式,那么可以通过@Qualifier注解指定qualifier name,通过@Named或者@Component(@Service,@Repository等)的value值指定bean name:

  1. @Component("lamborghini")
  2. @Qualifier("luxury")
  3. public class Lamborghini implements Car {
  4. }
复制代码

或者

  1. @Component
  2. @Named("lamborghini")
  3. @Qualifier("luxury")
  4. public class Lamborghini implements Car {
  5. }
复制代码

同样,如果没有指定bean name,那么Spring会默认是用类名首字母小写(Lamborghini=>lamborghini)。

三、 通过Anotation注入依赖的方式在XML注入方式之前进行。如果对同一个bean的依赖同时使用了两种注入方式,那么XML的优先。但是不同担心通过Anotation注入的依赖没法注入XML中配置的bean,依赖注入是在bean的注册之后进行的。

四、目前的autowired by type方式(笔者用的是3.2.3.RELEASE版本),Spring的AutowiredAnnotationBeanPostProcessor 实现都是有”bug”的,也就是说@Autowired和@Inject都是有坑的(称之为坑,不称之为bug是因为貌似是故意的。。)。这是来源于线上 的一个bug,也是这边文章的写作原因。现场如下:

application-context.xml中有如下定义:

  1. <xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:util="http://www.springframework.org/schema/util"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  8. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
  9. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  10. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
  11. <context:annotation-config />
  12. <context:component-scan base-package="me.arganzheng.study" />
  13. <util:constant id="en"
  14. static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN" />
  15. <util:constant id="ja"
  16. static-field="me.arganzheng.study.spring.autowired.Constants.Language.JP" />
  17. <util:constant id="ind"
  18. static-field="me.arganzheng.study.spring.autowired.Constants.Language.IND" />
  19. <util:constant id="pt"
  20. static-field="me.arganzheng.study.spring.autowired.Constants.Language.PT" />
  21. <util:constant id="th"
  22. static-field="me.arganzheng.study.spring.autowired.Constants.Language.TH" />
  23. <util:constant id="ar"
  24. static-field="me.arganzheng.study.spring.autowired.Constants.Language.AR" />
  25. <util:constant id="en-rIn"
  26. static-field="me.arganzheng.study.spring.autowired.Constants.Language.EN_RIN" />
  27. <util:map id="languageChangesMap" key-type="java.lang.String"
  28. value-type="java.lang.String">
  29. <entry key="pt" value="pt" />
  30. <entry key="br" value="pt" />
  31. <entry key="jp" value="ja" />
  32. <entry key="ja" value="ja" />
  33. <entry key="ind" value="ind" />
  34. <entry key="id" value="ind" />
  35. <entry key="en-rin" value="en-rIn" />
  36. <entry key="in" value="en-rIn" />
  37. <entry key="en" value="en" />
  38. <entry key="gb" value="en" />
  39. <entry key="th" value="th" />
  40. <entry key="ar" value="ar" />
  41. <entry key="eg" value="ar" />
  42. </util:map>
  43. </beans>
复制代码

其中static-field应用的常量定义在如下类中:

  1. package me.arganzheng.study.spring.autowired;
  2. public interface Constants {
  3. public interface Language {
  4. public static final String EN = "CommonConstants.LANG_ENGLISH";
  5. public static final String JP = "CommonConstants.LANG_JAPANESE";
  6. public static final String IND = "CommonConstants.LANG_INDONESIAN";
  7. public static final String PT = "CommonConstants.LANG_PORTUGUESE";
  8. public static final String TH = "CommonConstants.LANG_THAI";
  9. public static final String EN_RIN = "CommonConstants.LANG_ENGLISH_INDIA";
  10. public static final String AR = "CommonConstants.LANG_Arabic";
  11. }
  12. }
复制代码

然后如果我们在代码中如下声明依赖:

  1. public class AutowiredTest extends BaseSpringTestCase {
  2. @Autowired
  3. private Map<String, String> languageChangesMap;
  4. [url=home.php?mod=space&uid=5447]@test[/url]
  5. public void testAutowired() {
  6. notNull(languageChangesMap);
  7. System.out.println(languageChangesMap.getClass().getSimpleName());
  8. System.out.println(languageChangesMap);
  9. }
  10. }
复制代码

Guess what,诡异的事情发生了!

运行结果如下:

  1. LinkedHashMap
  2. {en=CommonConstants.LANG_ENGLISH, ja=CommonConstants.LANG_JAPANESE, ind=CommonConstants.LANG_INDONESIAN, pt=CommonConstants.LANG_PORTUGUESE, th=CommonConstants.LANG_THAI, ar=CommonConstants.LANG_Arabic, en-rIn=CommonConstants.LANG_ENGLISH_INDIA}
复制代码

也就是说Map

严重: Caught exception while allowing TestExecutionListener

  1. [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@5c51ee0a] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@6e301e0]
  2. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'me.arganzheng.study.spring.autowired.AutowiredTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.Map me.arganzheng.study.spring.autowired.AutowiredTest.languageChangesMap; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  3. ...
  4. ed by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
  5. at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
  6. at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
  7. at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
  8. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
  9. ... 28 more
复制代码

debug了一下,发现确实是Spring的一个bug。在DefaultListableBeanFactory的这个方法出问题了:

  1. protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
  2. Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
  3. ...
  4. else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
  5. Class<?> keyType = descriptor.getMapKeyType();
  6. if (keyType == null || !String.class.isAssignableFrom(keyType)) {
  7. if (descriptor.isRequired()) {
  8. throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
  9. "] must be assignable to [java.lang.String]");
  10. }
  11. return null;
  12. }
  13. Class<?> valueType = descriptor.getMapValueType();
  14. if (valueType == null) {
  15. if (descriptor.isRequired()) {
  16. throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
  17. }
  18. return null;
  19. }
  20. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
  21. if (matchingBeans.isEmpty()) {
  22. if (descriptor.isRequired()) {
  23. raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
  24. }
  25. return null;
  26. }
  27. if (autowiredBeanNames != null) {
  28. autowiredBeanNames.addAll(matchingBeans.keySet());
  29. }
  30. return matchingBeans;
  31. }
  32. ...
  33. }
复制代码

关键在这一句:Map

严重: Caught exception while allowing TestExecutionListener

  1. [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@9476189] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest@2d546e21]
  2. ...
  3. Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=languageChangesMap)}
  4. at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
  5. at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
  6. at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
  7. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
  8. ... 28 more
复制代码

debug了一下,发现跟没有指定qualifie name是一样的执行路径。不是指定了bean name了吗?为什么还是autowired by type呢?仔细查看了一下才发现。DefaultListableBeanFactory的doResolveDependency方法对首先对类型做 了区别:

  1. protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
  2. Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
  3. Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
  4. if (value != null) {
  5. if (value instanceof String) {
  6. String strVal = resolveEmbeddedValue((String) value);
  7. BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
  8. value = evaluateBeanDefinitionString(strVal, bd);
  9. }
  10. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  11. return (descriptor.getField() != null ?
  12. converter.convertIfNecessary(value, type, descriptor.getField()) :
  13. converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
  14. }
  15. if (type.isArray()) {
  16. Class<?> componentType = type.getComponentType();
  17. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
  18. if (matchingBeans.isEmpty()) {
  19. if (descriptor.isRequired()) {
  20. raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor);
  21. }
  22. return null;
  23. }
  24. if (autowiredBeanNames != null) {
  25. autowiredBeanNames.addAll(matchingBeans.keySet());
  26. }
  27. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  28. return converter.convertIfNecessary(matchingBeans.values(), type);
  29. }
  30. else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
  31. Class<?> elementType = descriptor.getCollectionType();
  32. if (elementType == null) {
  33. if (descriptor.isRequired()) {
  34. throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
  35. }
  36. return null;
  37. }
  38. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor);
  39. if (matchingBeans.isEmpty()) {
  40. if (descriptor.isRequired()) {
  41. raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor);
  42. }
  43. return null;
  44. }
  45. if (autowiredBeanNames != null) {
  46. autowiredBeanNames.addAll(matchingBeans.keySet());
  47. }
  48. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  49. return converter.convertIfNecessary(matchingBeans.values(), type);
  50. }
  51. else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
  52. Class<?> keyType = descriptor.getMapKeyType();
  53. if (keyType == null || !String.class.isAssignableFrom(keyType)) {
  54. if (descriptor.isRequired()) {
  55. throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
  56. "] must be assignable to [java.lang.String]");
  57. }
  58. return null;
  59. }
  60. Class<?> valueType = descriptor.getMapValueType();
  61. if (valueType == null) {
  62. if (descriptor.isRequired()) {
  63. throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
  64. }
  65. return null;
  66. }
  67. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor);
  68. if (matchingBeans.isEmpty()) {
  69. if (descriptor.isRequired()) {
  70. raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor);
  71. }
  72. return null;
  73. }
  74. if (autowiredBeanNames != null) {
  75. autowiredBeanNames.addAll(matchingBeans.keySet());
  76. }
  77. return matchingBeans;
  78. }
  79. else {
  80. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  81. if (matchingBeans.isEmpty()) {
  82. if (descriptor.isRequired()) {
  83. raiseNoSuchBeanDefinitionException(type, "", descriptor);
  84. }
  85. return null;
  86. }
  87. if (matchingBeans.size() > 1) {
  88. String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
  89. if (primaryBeanName == null) {
  90. throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
  91. }
  92. if (autowiredBeanNames != null) {
  93. autowiredBeanNames.add(primaryBeanName);
  94. }
  95. return matchingBeans.get(primaryBeanName);
  96. }
  97. // We have exactly one match.
  98. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
  99. if (autowiredBeanNames != null) {
  100. autowiredBeanNames.add(entry.getKey());
  101. }
  102. return entry.getValue();
  103. }
  104. }
复制代码

如果是Array,Collection或者Map,则根据集合类中元素的类型来进行autowired by type(Map使用value的类型)。为什么这么特殊处理呢?原来,Spring是为了达到这样的目的:让你可以一次注入所有符合类型的实现,也就是 说可以这样子注入:

@Autowired
private List cars;

如果你的car有多个实现,那么都会注入进来,不会再报

  1. org.springframework.beans.factory.NoSuchBeanDefinitionException:
  2. No unique bean of type [me.arganzheng.study.spring.autowired.Car] is defined:
  3. expected single matching bean but found 2: [audi, toyota].
复制代码

然而,上面的情况如果你用@Resource则不会有这个问题:

  1. public class AutowiredTest extends BaseSpringTestCase {
  2. @Resource
  3. @Qualifier("languageChangesMap")
  4. private Map<String, String> languageChangesMap;
  5. @Test
  6. public void testAutowired() {
  7. assertNotNull(languageChangesMap);
  8. System.out.println(languageChangesMap.getClass().getSimpleName());
  9. System.out.println(languageChangesMap);
  10. }
  11. }
复制代码

正常运行:

  1. LinkedHashMap
  2. {pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
复制代码

当然,你如果不指定@Qualifier(“languageChangesMap”),同时field name不是languageChangesMap,那么还是一样报错的。

  1. Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@javax.annotation.Resource(shareable=true, mappedName=, description=, name=, type=class java.lang.Object, authenticationType=CONTAINER, lookup=)}
  2. at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:986)
  3. at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:843)
  4. at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:768)
  5. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:438)
  6. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:416)
  7. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:550)
  8. at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:150)
  9. at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
  10. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:303)
  11. ... 26 more
复制代码

而且,@Resource也可以实现上面的List接收所有实现:

  1. public class AutowiredTest extends BaseSpringTestCase {
  2. @Resource
  3. @Qualifier("languageChangesMap")
  4. private Map<String, String> languageChangesMap;
  5. @Resource
  6. private List<Car> cars;
  7. @Test
  8. public void testAutowired() {
  9. assertNotNull(languageChangesMap);
  10. System.out.println(languageChangesMap.getClass().getSimpleName());
  11. System.out.println(languageChangesMap);
  12. assertNotNull(cars);
  13. System.out.println(cars.getClass().getSimpleName());
  14. System.out.println(cars);
  15. }
  16. }
复制代码

运行的妥妥的:

  1. LinkedHashMap
  2. {pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar}
  3. ArrayList
复制代码

[me.arganzheng.study.spring.autowired.Audi@579584da, me.arganzheng.study.spring.autowired.Toyota@19453122]
这是因为@Resource注解使用的是CommonAnnotationBeanPostProcessor处理器,跟 AutowiredAnnotationBeanPostProcessor不是同一个作者[/偷笑]。这里就不分析了,感兴趣的同学可以自己看代码研究 一下。

最终结论如下

1、@Autowired和@Inject

autowired by type 可以 通过@Qualifier 显式指定 autowired by qualifier name(非集合类。注意:不是autowired by bean name!)

如果 autowired by type 失败(找不到或者找到多个实现),则退化为autowired by field name(非集合类)

2、@Resource

默认 autowired by field name
如果 autowired by field name失败,会退化为 autowired by type
可以 通过@Qualifier 显式指定 autowired by qualifier name
如果 autowired by qualifier name失败,会退化为 autowired by field name。但是这时候如果 autowired by field name失败,就不会再退化为autowired by type了
测试工程保存在GitHub上,是标准的maven工程,感兴趣的同学可以clone到本地运行测试一下。

补充

有同事指出Spring官方文档上有这么一句话跟我的结有点冲突:

However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id.

也就是说@Autowired即使加了@Qualifier注解,其实也是autowired by type。@Qualifier只是一个限定词,过滤条件而已。重新跟进了一下代码,发现确实是这样子的。Spring设计的这个 @Qualifier name 并不等同于 bean name。他有点类似于一个tag。不过如果这个tag是唯一的化,那么其实效果上等同于bean name。实现上,Spring是先getByType,得到list candicates,然后再根据qualifier name进行过滤。

再定义一个兰博基尼,这里使用@Qualifier指定:

  1. package me.arganzheng.study.spring.autowired;
  2. import org.springframework.beans.factory.annotation.Qualifier;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Qualifier("luxury")
  6. public class Lamborghini implements Car {
  7. }
复制代码

再定义一个劳斯莱斯,这里故意用@Named指定:

  1. package me.arganzheng.study.spring.autowired;
  2. import javax.inject.Named;
  3. import org.springframework.stereotype.Component;
  4. @Component
  5. @Named("luxury")
  6. public class RollsRoyce implements Car {
  7. }
复制代码

测试一下注入定义的豪华车:

  1. package me.arganzheng.study.spring.autowired;
  2. import static junit.framework.Assert.assertNotNull;
  3. import java.util.List;
  4. import me.arganzheng.study.BaseSpringTestCase;
  5. import org.junit.Test;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.beans.factory.annotation.Qualifier;
  8. /**
  9. *
  10. * @author zhengzhibin
  11. *
  12. */
  13. public class AutowiredTest extends BaseSpringTestCase {
  14. @Autowired
  15. @Qualifier("luxury")
  16. private List<Car> luxuryCars;
  17. @Test
  18. public void testAutowired() {
  19. assertNotNull(luxuryCars);
  20. System.out.println(luxuryCars.getClass().getSimpleName());
  21. System.out.println(luxuryCars);
  22. }
  23. }
复制代码

运行结果如下:

  1. ArrayList
  2. [me.arganzheng.study.spring.autowired.Lamborghini@66b875e1, me.arganzheng.study.spring.autowired.RollsRoyce@58433b76]
复制代码

补充:Autowiring modes

Spring支持四种autowire模式,当使用XML配置方式时,你可以通过autowire属性指定。

  1. no. (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system.
  2. byName. Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property.
  3. byType. Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
  4. constructor. Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.
复制代码

如果使用@Autowired、@Inject或者@Resource注解的时候,则稍微复杂一些,会有一个失败退化过程,并且引入了Qualifier。不过基本原理是一样。

最新评论

小黑屋|在路上 ( 蜀ICP备15035742号-1 

;

GMT+8, 2025-7-7 01:34

Copyright 2015-2025 djqfx

返回顶部