`

spring中自动注入resource和autowire的区别

阅读更多
Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同
2012/11/07 17:25:28 No Comments
Tags: autowire, default-autowire, java, resource, spring, 源码分析   Posted :java开发, 开源框架, 编程开发

前面说到了关于在xml中有提供default-autowire的配置信息,从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.也可以由下面的代码得出:
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }
......
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    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规范,请看以下代码:


private TxInterface tx2;

public TxInterface getTx3() {
    return tx2;
}

public void setTx5(TxInterface tx3) {
    this.tx2 = tx3;
}

这里是不会进行任何注入的,因为里面的tx2,根本不满足bean规范。但如果将方法setTx5修改为setTx2,则就满足bean规范,就会进行byName注入了。

@Autowired注解

再来看Autowired注解,有的人说autowired注解是按照byType方式进行配置,其实这个说法是错的,至少是不完善的。为什么呢,这需要我们来查看整个autowire的流程,如以下代码示:

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
    if (descriptor.isRequired()) {
        raiseNoSuchBeanDefinitionException(type, "", descriptor);
    }
    return null;
}
if (matchingBeans.size() > 1) {
    String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);
    if (primaryBeanName == null) {
        throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " +
                matchingBeans.size() + ": " + matchingBeans.keySet());
    }
    if (autowiredBeanNames != null) {
        autowiredBeanNames.add(primaryBeanName);
    }
    return matchingBeans.get(primaryBeanName);
}
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
    autowiredBeanNames.add(entry.getKey());
}
return entry.getValue();

以上代码来自于类DefaultListableBeanFactory的doResolveDependency方法。这是由类AutowiredAnnotationBeanPostProcessor类通过调用inject方法时,需要通过调用beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter)来解析引用信息。我们仔细看以上的逻辑,可以从下面的顺序进行处理。

    首先根据类型找到所有可以满足条件的bean
    判断bean长度,如果没有,则根据@autowired中的required属性进行判断是否抛出异常(默认为true)
    如果多于一个,则尝试寻找最优的那一个,如果最优的未找到,则抛出异常
    如果只有一个,则直接使用此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)的情况下:

String name = element.name;

if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
        factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
    autowiredBeanNames = new LinkedHashSet<String>();
    resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
            element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
}
else {
    resource = factory.getBean(name, element.lookupType);
    autowiredBeanNames = Collections.singleton(name);
}

if (factory instanceof ConfigurableBeanFactory) {
    ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
    for (String autowiredBeanName : autowiredBeanNames) {
        beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
    }
}

return resource;

以上代码来自于类CommonAnnotationBeanPostProcessor中的autowireResource方法,是由此类通过getResourceToInject获取将要注入的bean来调用的。上面的方法详细描述了整个过程,如下所示:

    获取element的名称,判断beanFactory是否存在此name的bean
    如果存在,则直接使用此name进行查询
    否则退化到默认的autowire查找方式

从上面的第三步,可以看出,Resource在没有根据name查找到的情况下,会走Autowire的方式。所以,从范围来看Resouce的查找范围比Autowire范围更大。

再来看第1步,获取element的名称,这里说是element的名称,因为它的来源有2个地方。一是在resouce注解中配置的name属性,第二就是setter名称或者是field名称(取决于@Resource的配置地点),。这里说的是setter名称,而不是属性名称,这就是需要注意的地方。来源代码如下所示:

String resourceName = resource.name();
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
    resourceName = this.member.getName();
    if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
        resourceName = Introspector.decapitalize(resourceName.substring(3));
    }
}

来源于类ResourceElement的initAnnotation方法。因此,如果方法为如下所示:


@Resource
public void setTx2(TxInterface tx5) {
    this.tx4 = tx5;
}

则获取到的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。不能够简单地混为一谈。
分享到:
评论

相关推荐

    (转)Spring中@Autowired注解和@Resource注解的区别

    在Spring框架中,`@Autowired`和`@Resource`注解是两个常见的依赖注入(DI, Dependency Injection)工具,它们都是用来解决组件之间的耦合问题,使得代码更加灵活和可测试。然而,这两个注解在具体使用时有一些关键性...

    spring-autowire.zip

    在这里,Spring容器会自动查找类型匹配的bean(Cat和Dog),并将它们注入到Person的相应字段中。如果存在多个同类型的bean,可以通过`@Qualifier`注解指定具体的bean,或者通过`@Primary`标记一个bean为默认首选。 ...

    autowire自动导入句柄

    在Spring框架中,`autowire`是一种自动装配bean依赖的方式,它极大地简化了bean之间的依赖注入过程。本文将深入探讨`autowire`自动导入句柄及其在实际开发中的应用。 ### 1. `autowire`简介 `autowire`是Spring...

    20 面试官,请不要再问我@Resource和@Autowire注解的区别了慕课专栏(1)1

    在Java Spring框架中,`@Resource`和`@Autowired`都是用于依赖注入(DI)的注解,但它们有着不同的用途和工作原理。本篇文章主要关注`@Resource`注解,探讨其使用方法和实现机制。 `@Resource`注解通常用于按名称注入...

    3Spring使用annotation方式autowire

    本篇将详细探讨"3Spring使用annotation方式autowire"这一主题,包括注解驱动的自动装配(Autowiring)以及相关的源码分析。 ### 一、注解驱动的自动装配 自动装配是Spring框架的一个核心特性,它允许框架自动管理...

    Spring2.5 自动扫描classpath

    5. **Spring_demo_03_autowire** - 自动装配(Autowiring)是Spring自动扫描classpath的核心特性,它能根据类型或名称自动为bean注入依赖。 6. **Spring_demo_08_autowired_qualifier** - @Autowired注解配合@...

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1. @Configurable object的单元测试 6.8.1.2. 多application context情况下的处理 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来...

    storm-spring-autowire:使storm支持spring的注入功能

    storm-spring-autowire 使storm支持spring的注入功能 由于storm的生命周期如下 1.在提交了一个topology之后(是在nimbus所在的机器么?), 创建spout/bolt实例(spout/bolt在storm中统称为component)并进行序列化. 2.将...

    自动装配 AutoWire

    **自动装配(Autowired)是Spring框架中的一个重要特性,它允许我们自动将依赖注入到bean中,而无需显式地在配置文件中指定依赖关系。在本文中,我们将深入探讨Autowired的工作原理、使用方式以及它如何简化Java应用的...

    Spring的Autowired自动装配(XML版本+Annotation版本+源码+解析)

    在Spring框架中,`Autowired`是自动装配bean的重要机制,它极大地简化了依赖注入的过程。本篇文章将深入探讨XML版本、注解版本的`Autowired`以及相关源码解析。 首先,我们来理解`Autowired`的基本概念。在Spring...

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    Spring学习笔记之二“属性注入”

    在Spring框架中,属性注入是核心特性之一,它允许我们为bean对象动态地设置属性值,无需在代码中使用new关键字实例化对象并手动设置属性。这种依赖注入(Dependency Injection,DI)的设计模式大大增强了软件的可...

    strut2和spring整合

    Struts2 和 Spring 整合是为了在 Struts2 框架中利用 Spring 的依赖注入(Dependency Injection,DI)和管理bean的能力,从而提高应用程序的灵活性和可维护性。以下是整合过程中涉及的关键知识点: 1. **Struts2-...

    spring ioc使用教程

    通过XML配置、注解和自动装配等方式,Spring可以轻松地管理对象的生命周期和依赖关系,从而降低了系统的复杂性。 理解并熟练运用这些知识点,能够帮助开发者更好地掌握Spring框架,提升后端开发效率。在实际项目中...

    Spring中XML配置文件的十二个最佳方法.doc

    在Spring框架中,XML配置文件是应用的核心组成部分,用于定义和组织Bean的生命周期和依赖关系。本文将详细解析Spring中XML配置文件的十二个最佳实践,主要关注前六个方法。 1. **避免使用自动绑定(Autowiring)** ...

    spring个人笔记,很好的哦

    例如,在UserManager类中,我们可以通过提供构造函数或setter方法,让Spring自动将实例化的UserDao注入进来。为了实现这一功能,必须在Spring配置文件中定义这些对象及其依赖关系。 Spring Ioc(Inversion of ...

    spring2.5 学习笔记

    - **自动装配(autowire)**:Spring可以自动匹配并注入bean,包括byName、byType等方式。 - **生命周期管理**:Spring提供了管理bean生命周期的方法,如初始化和销毁方法。 #### 使用注解配置Spring Spring 2.5...

    如何完成spring的最小化XML配置

    Spring框架的XML配置是其核心特性之一,它允许开发者声明Bean的定义,管理依赖关系以及配置其他服务。...通过理解不同类型的自动装配和注解的使用,开发者能够更好地控制和管理Spring容器中的Bean依赖关系。

    马士兵Spring,XML部分整理

    本文将深入探讨Spring XML配置中的核心概念,包括Bean的注入、自动装配、集合注入、AOP(面向切面编程)以及事务管理。 1. **Bean注入**:在Spring中,Bean的注入主要通过setter方法完成。例如,我们可以在XML配置...

Global site tag (gtag.js) - Google Analytics