@Autowired @Resource @Inject的区别

测试环境:Intellij + gradle


编写build.gradle

group 'com.xiya'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile group: 'org.springframework', name: 'spring-context', version: '4.3.7.RELEASE'
}


Spring存在三种依赖注入的注解:

@Autowired:org.springframework.beans.factory.annotation.Autowired

@Resource:javax.annotation.Resource

@Inject:javax.inject.Inject

三种方式的区别在哪里呢?


测试:

定义Person接口

package com.xiya.entity;

/**
* @file Person.java
* @CopyRight (C) http://blog.csdn.net/x_iya
* @Description
* @author N3verL4nd
* @email lgh1992314@qq.com
* @date 2017/6/17
*/
public interface Person {
    void sayHello();
}

定义两个实现类:

package com.xiya.entity;

import org.springframework.stereotype.Component;

/**
* @file Chinese.java
* @CopyRight (C) http://blog.csdn.net/x_iya
* @Description
* @author N3verL4nd
* @email lgh1992314@qq.com
* @date 2017/6/17
*/

@Component
public class Chinese implements Person {
    @Override
    public void sayHello() {
        System.out.println("你好,我来自中国!");
    }
}

package com.xiya.entity;

import org.springframework.stereotype.Component;

/**
* @file American.java
* @CopyRight (C) http://blog.csdn.net/x_iya
* @Description
* @author N3verL4nd
* @email lgh1992314@qq.com
* @date 2017/6/17
*/

@Component
public class American implements Person {
    @Override
    public void sayHello() {
        System.out.println("Hello, I come from America!");
    }
}

定义Service层

package com.xiya.service;

import com.xiya.entity.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
* @file PersonManager.java
* @CopyRight (C) http://blog.csdn.net/x_iya
* @Description
* @author N3verL4nd
* @email lgh1992314@qq.com
* @date 2017/6/17
*/

@Service
public class PersonManager {
    private Person person;

    /* 构造器注入
     * 使用:
     * @Autowired
     * @Qualifier("chinese")
     * 或者使用:
     * @Resource(name = "chinese")
     *
     */

    @Resource(name = "chinese")
    public void setPeople(Person person) {
        this.person = person;
    }

    public void sayHello() {
        person.sayHello();
    }

}


applicationContext.xml

<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置自动扫描的包-->
    <context:component-scan base-package="com.xiya.entity, com.xiya.service">
        <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>-->
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>-->
    </context:component-scan>
</beans>

测试类:

import com.xiya.service.PersonManager;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class T {
    private ApplicationContext context;

    @Before
    public void setBefore() {
        context = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void test() {
        PersonManager peopleManager = context.getBean(PersonManager.class);
        peopleManager.sayHello();
    }
}

目录结构:


对于@Autowired

先按类型注入,然后按照名称注入,都无法找到唯一的一个实现类则报错。

我们将American类中@Component注释掉,这样在Spring环境中Person只有Chinese一个实现类

输出:你好

因为只有Chinese实现了Person,所以会正确运行(通过类型查找)。
我们取消American类中@Component的注释,运行程序会出现异常:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'people': Unsatisfied dependency expressed through field 'person'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.xiya.spring.entity.Person' available:expected single matching bean but found 2: american,chinese
因为Person有American和Chinese两个实现类,Spring不知道该用哪一个进行注入。接着按照名称注入,spring容器中没有名字为person的bean。
People修改如下:(Chinese与American类都用@Component注解)

package com.xiya.service;

import com.xiya.entity.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
* @file PersonManager.java
* @CopyRight (C) http://blog.csdn.net/x_iya
* @Description
* @author N3verL4nd
* @email lgh1992314@qq.com
* @date 2017/6/17
*/

@Service
public class PersonManager {
    private Person person;

    /* 构造器注入
     * 使用:
     * @Autowired
     * @Qualifier("chinese")
     * 或者使用:
     * @Resource(name = "chinese")
     *
     */

//    @Resource(name = "chinese")
    @Autowired
//    @Qualifier("chinese")
    public void setPeople(Person chinese) {
        this.person = chinese;
    }

    public void sayHello() {
        person.sayHello();
    }

}

结果会输出:“你好,我来自中国!”。此时变量名chinese等于Chinese默认的Qualifier名字。改为american则输出“Hello, I come from America!”。

相当于:

@Qualifier("chinese")

如果只有一个实体类Chinese(删除American类),而且Chinese不实现Person接口,此时怎么注入Person chinese都会出错(请求的是Person对象,注入的却不是)。

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'people': Unsatisfied dependency expressed through field 'chinese'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xiya.spring.entity.Person' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

查找流程:首先在Spring容器内查找类型为Person的bean,没有找到;接着按照chinese名称进行查找,虽然查找到,但是是类型为Chinese,抛异常。

@Autowired(required=false)中如果设置required为false(默认为true),则注入失败时不会抛出异常,但person.sayHello();调用时会出现空指针异常NullPointerException。


对于@Inject

需要引入:

compile group: 'javax.inject', name: 'javax.inject', version: '1'

在Spring的环境下,@Inject和@Autowired是相同的,都是使用AutowiredAnnotationBeanPostProcessor来处理依赖注入,@Inject是jsr-330定义的规范,还是比较推荐使用这种方式进行依赖注入,如果使用这种方式,切换到Guice也是可以的。
如果硬要说两个的区别,首先@Inject是Java EE包里的,在SE环境需要单独引入。另一个区别在于@Autowired可以设置required=false而@Inject并没有这个设置选项。

对于@Resource

先按名字注入,再按类型注入,都无法找到唯一的一个出现异常。
这是jsr-250定义的规范,相对于jsr-330算是比较老的了。这里不推荐使用这种注入方式,下面讨论一下其注入的问题。
首先我们注释American里的@Component,这样在Spring托管的Bean里只有Chinese实现了Person接口,测试用例如下:

@Component
public class People {
    @Resource
    private Person person;
    public void sayHello() {
        person.sayHello();
    }
}
输出结果:“你好,我来自中国!”。 此时@Resource先按名字person,并未找到名称为person的bean,然后按照类型来找,只有Chinese,注入成功。

取消American中的@Component注释,出现如下异常:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'people': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.xiya.spring.entity.Person' available: expected single matching bean but found 2: american,chinese

此时先通过名字无法确定,然后通过类型还是无法确定,抛出异常。

修改测试代码:

@Component
public class People {
    @Resource
    private Person chinese;
    public void sayHello() {
        chinese.sayHello();
    }
}
输出结果你好,此时按名字找到了chinese。


--------------


@Autowired是可以写在字段或者setter方法上或者构造器上。

然而,在字段上使用@Autowired注解,IDEA会提示“Field injection is not recommended.Spring Team recommends:’Always use constructor based dependency injection in your beans.Always use assertions for mandatory dependencies.’” 不推荐使用字段注入,会发生空指针错误,推荐使用构造器注入。

@Component
public class People {
    @Autowired
    @Qualifier("chinese")
    private Person person;

    public void sayHello() {
        person.sayHello();// NullPointerException发生
    }
}
使用构造器注入则可以避免该问题的发生:

@Component
public class People {
    /*@Autowired
    @Qualifier("chinese")*/
    private Person person;

    @Autowired
    public People(@Qualifier("chinese") Person person) {
        Assert.notNull(person, "person must not be null");
        this.person = person;
    }

    public void sayHello() {
        person.sayHello();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值