@Autowired @Resource @Inject的区别

来源:互联网 发布:php取字符串的后几位 编辑:程序博客网 时间:2024/05/17 01:16

测试环境:Intellij + gradle


编写build.gradle

group 'com.xiya'version '1.0-SNAPSHOT'apply plugin: 'java'apply plugin: 'idea'sourceCompatibility = 1.8repositories {    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*/@Componentpublic 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*/@Componentpublic 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*/@Servicepublic 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*/@Servicepublic 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接口,测试用例如下:

@Componentpublic 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

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

修改测试代码:

@Componentpublic 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.’” 不推荐使用字段注入,会发生空指针错误,推荐使用构造器注入。

@Componentpublic class People {    @Autowired    @Qualifier("chinese")    private Person person;    public void sayHello() {        person.sayHello();// NullPointerException发生    }}
使用构造器注入则可以避免该问题的发生:

@Componentpublic 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();    }}

0 0
原创粉丝点击