zoukankan      html  css  js  c++  java
  • Spring @Resource, @Autowired and @Inject 注入

    Overview

    I’ve been asked several times to explain the difference between injecting Spring beans with ‘@Resource’, ‘@Autowired’, and ‘@Inject’. While I received a few opinions from colleagues and read a couple of posts on this topic I didn’t feel like I had a complete picture.

    Annotations

    AnnotationPackageSource
    @Resource javax.annotation Java
    @Inject javax.inject Java
    @Qualifier javax.inject Java
    @Autowired org.springframework.bean.factory Spring



    In order to explore the behavior of each annotation I fired up Spring Tool Suite and started debugging the code. I used Spring 3.0.5.RELEASE in my research. The following is a summary of my findings.

    The Code

    I wanted to know how ‘@Resource’, ‘@Autowired’, and ‘@Inject’ resolved dependencies. I created an interface called ‘Party’ and created two implementations classes. This allowed me to inject beans without using the concrete type. This provided the flexibility I needed to determine how Spring resolves beans when there are multiple type matches.

    public interface Party { }

    ‘Person’ is a component and it implements ‘Party’.

    package com.sourceallies.person;...
    @Component
    public class Person implements Party { }

    ‘Organization’ is a component and it implements ‘Party’.

    package com.sourceallies.organization;...
    @Component
    public class Organization implements Party { }

    I setup a Spring context that scans both of these packages for beans marked with ‘@Component’.

    <context:component-scan base-package="com.sourceallies.organization"/>
    <context:component-scan base-package="com.sourceallies.person"/>

    Tests

    Test 1: Ambiguous Beans

    In this test I injected a ‘Party’ bean that has multiple implementations in the Spring context.

    @Resource
    private Party party;
    @Autowired
    private Party party;
    @Inject
    private Party party;

    In all three cases a ‘NoSuchBeanDefinitionException’ is thrown. While this exception’s name implies that no beans were found, the message explains that two beans were found. All of these annotations result in the same exception.

    org.springframework.beans.factory.NoSuchBeanDefinitionException: 
    No unique bean of type [com.sourceallies.Party] is defined: expected single matching bean but found 2: [organization, person]

    Test 2: Field Name

    In this test I named the Party field person. By default beans marked with ‘@Component’ will have the same name as the class. Therefore the name of the class ‘Person’ is person.

    @Resource
    private Party person;
    @Autowired
    private Party person;
    @Inject
    private Party person;

    ‘@Resource’ can also take an optional ‘name’ attribute. This is equivalent to the ‘@Resource’ code above. In this case the field variable name remains ‘party’. There is no equivalent syntax for ‘@Autowired’ or ‘@Inject’. Instead you would have to use a ‘@Qualifier’. This syntax will be covered later.

    @Resource(name="person")
    private Party party;

    All four of these styles inject the ‘Person’ bean.

    Test 3: Field Type

    In this test I changed the type to be a ‘Person’.

    @Resource
    private Person party;
    @Autowired
    private Person party;
    @Inject
    private Person party;

    All of these annotations inject the ‘Person’ bean.

    Test 4: Default Name Qualifier

    In this test I use a ‘@Qualifier’ annotation to point to the default name of the ‘Person’ component.

    @Resource
    @Qualifier("person")
    private Party party;
    @Autowired
    @Qualifier("person")
    private Party party;
    @Inject
    @Qualifier("person")
    private Party party;

    All of these annotations inject the ‘Person’ bean.

    Test 5: Qualified Name

    I added a ‘@Qualifier’ annotation to the ‘Person’ class

    package com.sourceallies.person;...
    @Component
    @Qualifier("personBean")
    public class Person implements Party { }

    In this test I use a ‘@Qualifier’ annotation to point to the qualified name of the ‘Person’ component.

    @Resource
    @Qualifier("personBean")
    private Party party;
    @Autowired
    @Qualifier("personBean")
    private Party party;
    @Inject
    @Qualifier("personBean")
    private Party party;

    All of these annotations inject the ‘Person’ bean.

    Test 6: List of Beans

    In this test I inject a list of beans.

    @Resource
    private List<Party> parties;
    @Autowired
    private List<Party> parties;
    @Inject
    private List<Party> parties;

    All of these annotations inject 2 beans into the list. This can also be accomplished with a ‘@Qualifier’. Each bean marked with a specific qualifier will be added to the list.

    Test 7: Conflicting messages

    In this test I add a bad ‘@Qualifier’ and a matching field name.

    @Resource
    @Qualifier("bad")
    private Party person;
    @Autowired
    @Qualifier("bad")
    private Party person;
    @Inject
    @Qualifier("bad")
    private Party person;

    In this case the field marked with ‘@Resource’ uses the field name and ignores the ‘@Qualifier’. As a result the ‘Person’ bean is injected.

    However the ‘@Autowired’ and ‘@Inject’ field throw a ‘NoSuchBeanDefinitionException’ error because it can not find a bean that matches the ‘@Qualifier’.

     org.springframework.beans.factory.NoSuchBeanDefinitionException: 
     No matching bean of type [com.sourceallies.Party] found for dependency: 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=bad)}

    Conclusions

    With the exception of test 2 & 7 the configuration and outcomes were identical. When I looked under the hood I determined that the ‘@Autowired’ and ‘@Inject’ annotation behave identically. Both of these annotations use the ‘AutowiredAnnotationBeanPostProcessor’ to inject dependencies. ‘@Autowired’ and ‘@Inject’ can be used interchangeable to inject Spring beans. However the ‘@Resource’ annotation uses the ‘CommonAnnotationBeanPostProcessor’ to inject dependencies. Even though they use different post processor classes they all behave nearly identically. Below is a summary of their execution paths.

    @Autowired and @Inject

    1. Matches by Type

    2. Restricts by Qualifiers

    3. Matches by Name

    @Resource

    1. Matches by Name

    2. Matches by Type

    3. Restricts by Qualifiers (ignored if match is found by name)

    While it could be argued that ‘@Resource’ will perform faster by name than ‘@Autowired’ and ‘@Inject’ it would be negligible. This isn’t a sufficient reason to favor one syntax over the others. I do however favor the ‘@Resource’ annotation for it’s concise notation style.

    @Resource(name="person")
    @Autowired
    @Qualifier("person")
    @Inject
    @Qualifier("person")

    You may argue that they can be equal concise if you use the field name to identify the bean name.

    @Resource
    private Party person;
    @Autowired
    private Party person;
    @Inject
    private Party person;

    True enough, but what happens if you want to refactor your code? By simply renaming the field name you’re no longer referring to the same bean. I recommend the following practices when wiring beans with annotations.

    Spring Annotation Style Best Practices

    1. Explicitly name your component [@Component(“beanName”)]

    2. Use ‘@Resource’ with the ‘name’ attribute [@Resource(name=”beanName”)]

    3. Avoid ‘@Qualifier’ annotations unless you want to create a list of similar beans. For example you may want to mark a set of rules with a specific ‘@Qualifier’ annotation. This approach makes it simple to inject a group of rule classes into a list that can be used for processing data.

    4. Scan specific packages for components [context:component-scan base-package=”com.sourceallies.person”]. While this will result in more component-scan configurations it reduces the chance that you’ll add unnecessary components to your Spring context.

    Following these guidelines will increase the readability and stability of your Spring annotation configurations.

    附:中文解说

    spring2.5提供了基于注解(Annotation-based)的配置,我们可以通过注解的方式来完成注入依赖。在Java代码中可以使用 @Resource或者@Autowired注解方式来经行注入。虽然@Resource和@Autowired都可以来完成注入依赖,但它们之间是有区 别的。首先来看一下:

     a。@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入;

     b。@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用;

     c。@Resource注解是又J2EE提供,而@Autowired是由Spring提供,故减少系统对spring的依赖建议使用  

           @Resource的方式;

    d。 @Resource和@Autowired都可以书写标注在字段或者该字段的setter方法之上

    2、使用注解的方式,我们需要修改spring配置文件的头信息,修改部分红色标注,如下

       <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-2.5.xsd">
                   
    <context:annotation-config/>
         
    </beans>

    3、修改以上配置文件的头信息后,我们就可以在Java代码通过注解方式来注入bean,看下面代码

    (1)@Resource

    public class StudentService3 implements IStudentService {

      //@Resource(name="studentDao")放在此处也是可行的
        private IStudentDao studentDao;


    private String id;


    public void setId(String id) {
    this.id = id;
    }

     @Resource(name="studentDao") // 通过此注解完成从spring配置文件中查找名称为studentDao的bean来装配字段studentDao,如果spring配置文件中不存在 studentDao名称的bean则转向按照bean类型经行查找
     public void setStudentDao(IStudentDao studentDao) {
    this.studentDao = studentDao;
    }


    public void saveStudent() {
    studentDao.saveStudent();
    System.out.print(",ID 为:"+id);
    }


    }

    配置文件添加如下信息

      <bean id="studentDao" class="com.wch.dao.impl.StudentDao"></bean>
    <bean id="studentService3" class="com.wch.service.impl.StudentService3" />

    (2)@Autowired

    public class StudentService3 implements IStudentService {

      //@Autowired放在此处也是可行的
        private IStudentDao studentDao;


    private String id;


    public void setId(String id) {
    this.id = id;
    }

     @Autowired//通过此注解完成从spring配置文件中 查找满足studentDao类型的bean

      //@Qualifier("studentDao")则按照名称经行来查找转配的
     public void setStudentDao(IStudentDao studentDao) {
    this.studentDao = studentDao;
    }


    public void saveStudent() {
    studentDao.saveStudent();
    System.out.print(",ID 为:"+id);
    }


    }

    配置文件添加如下信息

    <bean id="studentDao" class="com.wch.dao.impl.StudentDao"></bean>
    <bean id="studentService3" class="com.wch.service.impl.StudentService3" />

    在java代码中可以使用@Autowire或者@Resource注解方式进行装配,这两个注解的区别是:
    @Autowire 默认按照类型装配,默认情况下它要求依赖对象必须存在如果允许为null,可以设置它required属性为false,如果我们想使用按照名称装配,可 以结合@Qualifier注解一起使用;


    @Resource默认按照名称装配,当找不到与名称匹配的bean才会按照类型装配,可以通过name属性指定,如果没有指定name属 性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找 依赖对象.

    注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖的对象时候,会回退到按照类型装配,但一旦指定了name属性,就只能按照名称 装配了.

  • 相关阅读:
    1-素材库同步:将素材组的素材同步到oss
    MongoDB_2:mongodb高级聚合查询
    关于python:如果键存在,则删除字典项
    Kafka学习-分布式日志系统 / 消息队列
    摘要算法—md5简介
    mac使用pyenv安装和管理多个python版本
    如何mac电脑上查看安装了几个python?
    mac os-Homebrew的安装及使用
    第一次博客
    个人介绍
  • 原文地址:https://www.cnblogs.com/brant/p/5723921.html
Copyright © 2011-2022 走看看