• 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方法上

  • 相关阅读:
    NSString的几种常用方法
    ios页面间传递参数四种方式
    ios category类别的使用
    iOS: 枚举类型 enum,NS_ENUM,NS_OPTIONS
    IOS导航栏的使用方法
    报错:Expected one result (or null) to be returned by selectOne(), but found: 6
    Spring小知识
    spring动态代理
    登录和注册功能思路
    sql语句实例练习
  • 原文地址:https://www.cnblogs.com/tanzq/p/8299293.html
Copyright © 2020-2023  润新知