Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同

时间:2021-12-15 22:30:54
前面说到了关于在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.也可以由下面的代码得出:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
        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规范,请看以下代码:
1
2
3
4
5
6
7
8
9
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的流程,如以下代码示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)的情况下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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名称,而不是属性名称,这就是需要注意的地方。来源代码如下所示:
1
2
3
4
5
6
7
8
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方法。因此,如果方法为如下所示:
1
2
3
4
@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注解和xml default-autowire工作方式异同的更多相关文章

  1. Spring中&commat;Autowired、&commat;Resource和&commat;Inject注解的使用和区别

    在使用Spring进行项目开发的时候,会大量使用到自动装配,那自动装配是什么呢?简单来说:Spring 利用依赖注入(DI)功能,完成SpringIOC容器中各个组件之间的依赖关系赋值管理. 下面介绍 ...

  2. Spring中&commat;Autowired与&commat;Resource的区别

    1.@Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上. 2.@Autowired默认按类型装配(这个注解是属业spring的),默认情况下必 ...

  3. spring中&commat;Autowired与 &commat;Resource区别

    @Autowired 与@Resource的区别: 1. @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上. 2. @Autowired默认 ...

  4. Spring中&commat;Autowired和&commat;Resource两种自动装配的方法

    @Autowired 默认按bean类型查找并注入,若此时有多个相同类型的bean时,按bean name查找则为:@Autowired @Qulifer(value=”bean名称”). @Reso ...

  5. Spring中&commat;Autowired注解、&commat;Resource注解的区别 (zz)

    Spring中@Autowired注解.@Resource注解的区别 Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource.@ ...

  6. Spring中&commat;Autowired注解与自动装配

    1 使用配置文件的方法来完成自动装配我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. ...

  7. Spring 注释 &commat;Autowired 和&commat;Resource 的区别

    Spring 注释 @Autowired 和@Resource 的区别 一. @Autowired和@Resource都可以用来装配bean,都可以写在字段上,或者方法上. 二. @Autowired ...

  8. Spring中 &commat;Autowired注解与J2EE&commat;Resource注解的区别

    在开发中经常使用到@Autowired和@Resource进行装配. 不禁好奇这两个注解的差异在何处??? 相同点: @Resource的作用相当于@Autowired,均可标注在字段或属性的sett ...

  9. Spring中&commat;Autowired注解、&commat;Resource注解的区别

    Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource.@PostConstruct以及@PreDestroy. @Resour ...

随机推荐

  1. something

    var colors=['red','green','yellow']; console.log(colors)//['red','green','yellow'] console.log(color ...

  2. web&period;xml相关知识摘录整理

    web.xml 中的listener. filter.servlet 加载顺序及其详解 在项目中总会遇到一些关于加载的优先级问题,近期也同样遇到过类似的,所以自己查找资料总结了下,下面有些是转载其他人 ...

  3. IntelliJ IDEA 快捷键和设置

    IntelliJ IDEA 使用总结 http://my.oschina.net/xianggao/blog/97539 IntelliJ IDEA 问题解决:1.乱码,主要是快捷键的字样显示乱码 中 ...

  4. ANDROID 通过监听来电去电,并弹出悬浮窗

    要监听android打电话和接电话,有一种的是通过新建一个Receiver继承自BroadcastReceiver. 还有一种也可通过PhoneStateListener来实现.今天就说说后面一种,废 ...

  5. Collection Views and Building Custom Layouts-备

    UICollectionView的结构回顾 首先回顾一下Collection View的构成,我们能看到的有三个部分: Cells Supplementary Views 追加视图 (类似Header ...

  6. 现有有N个学生的数据记录,每个记录包括学号、姓名、三科成绩。 编写一个函数input&comma;用来输入一个学生的数据记录。 编写一个函数print&comma;打印一个学生的数据记录。 在主函数调用这两个函数,读取N条记录输入,再按要求输出。 N&lt&semi;100

    #include <iostream> using namespace std; struct student {char num[100];  char name[100];  int ...

  7. MEMS加速度计工作原理

    一般加速度计有两块芯片组成,一块是MEMS传感器,另一块是客户化的信号处理芯片. 加速度计也称惯性传感器,因为它的工作原理就是靠MEMS中可移动部分的惯性.由于中间电容板的质量很大,而且它是一种悬臂构 ...

  8. 跟我开发NSP(网上查询平台):如何选择开发项目

    我想通过一个真实的项目开发的全过程,记录一下开发过程的点点滴滴,记录一下过程中的前思后想.这个全过程包括,如何选择项目.如何分析项目.如何组织项目开发.如何设计开发流程.如何设计软件的总体架构.如何建 ...

  9. 关于jquery的each的操作;

    <!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. Bounding-box 回归

    R-CNN系列均训练了Bounding-box回归器来对窗口进行校正,其目标是学习一种转换关系将预测得到的窗口P映射为真实窗口G(Ground truth). 变换方式 可以通过简单的仿射变换以及指数 ...