IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 为什么IDEA和Spring都不推荐使用@Autowired注入 -> 正文阅读

[Java知识库]为什么IDEA和Spring都不推荐使用@Autowired注入

背景

在IDEA中,如果使用@Autowired注入的时候会出现以下警告:
Field injection is not recommended (字段注入是不被推荐的)
在这里插入图片描述

但是使用@Resource却不会出现此提示,我现在的公司也是全都使用构造器注入的方式,而不是@Autowired的方式。

Spring常见的注入方式

setter方式注入
调用Setter的方法注入依赖

构造器注入
利用构造方法的参数注入依赖

属性注入/字段注入
在字段上使用@Autowired/Resource注解

演示@Autowired 所有装配方式

@Autowired字段注入

//这里使用配置类或者@Component注册bean都可以
@Data
@AllArgsConstructor
public class TestAutowiredFieldInject {
    private String desc;
}
@Data
@AllArgsConstructor
public class TestAutowiredNoClearNameFieldInject {
    private String desc;
}
@Data
@AllArgsConstructor
public class TestAutowiredNotRequiredFieldInject {
    private String desc;
}



@Slf4j
@Service
public class TestFieldInjectService {
    /**
     * 演示@Autowired字段注入
     */
    @Autowired
    private TestAutowiredFieldInject testAutowiredFieldInject;

    /**
     * 测试@Autowired不同名称字段注入
     */
    @Autowired
    private TestAutowiredNoClearNameFieldInject testAutowiredNoClearNameFieldInject;

    /**
     * 测试@Autowired非必须字段注入
     */
    @Autowired(required = false)
    private TestAutowiredNotRequiredFieldInject testAutowiredNotRequiredFieldInject;


    public void printObject() {
        log.info("对象:{},是否为空:{},对象信息:{}", "testAutowiredFieldInject", Objects.isNull(testAutowiredFieldInject), testAutowiredFieldInject);
        //这里testAutowiredNoClearNameFieldInject实际bean名称是testAutowiredFieldMeme,但是注入成功
        log.info("对象:{},是否为空:{},对象信息:{}", "testAutowiredNoClearNameFieldInject", Objects.isNull(testAutowiredNoClearNameFieldInject), testAutowiredNoClearNameFieldInject);
        //这里没有注入testAutowiredNotRequiredFieldInject,但是不会报错,如果去掉required=false,会启动应用不成功
        log.info("对象:{},是否为空:{},对象信息:{}", "testAutowiredFieldInject", Objects.isNull(testAutowiredNotRequiredFieldInject), testAutowiredNotRequiredFieldInject);
    }
}

结果:
在这里插入图片描述

@Autowired自动装配是通过byType方式(变量名修改不会影响注入结果,由此可以证明是byType而不是byName)
@Autowired正常情况下是强依赖型的,也就是说如果不存在TestAutowiredFieldInject 的依赖,那么将会启动应用不成功。
此时可以添加属性:@Autowired(required = false),表示非必须,可以启动成功。(找不到bean的话是null)

@Autowired通过@Qualifier设置为byName注入

配置类:

    /**
     * 测试@QualifierName注入
     */
    @Bean
    public TestQualifierNameInject testQualifierNameInjectx() {
        return new TestQualifierNameInject("TestQualifierNameInject x");
    }


    @Bean
    public TestQualifierNameInject testQualifierNameInjecty() {
        return new TestQualifierNameInject("TestQualifierNameInject y");
    }

测试程序:

@Slf4j
@Service
public class TestFieldInjectService {
    /**
     * 测试@QualifierName注入
     *
     * @author zhengbingyuan
     * @date 2022/9/29
     */
    @Autowired
    @Qualifier("testQualifierNameInjecty")
    private TestQualifierNameInject testQualifierNameInject;


    /**
     * 测试@QualifierName注入
     *
     * @author zhengbingyuan
     * @date 2022/9/29
     */
    @Autowired(required = false)
    @Qualifier("testQualifierNameInjectz")
    private TestQualifierNameInject testQualifierNameInjectz;


    public void printObjectQualifier() {
        //这里注入的是testQualifierNameInjecty
        log.info("对象:{},是否为空:{},对象信息:{}", "testQualifierNameInject", Objects.isNull(testQualifierNameInject), testQualifierNameInject);
        //这里testQualifierNameInjectz没有注册,也不会通过byType去匹配
        log.info("对象:{},是否为空:{},对象信息:{}", "testQualifierNameInjectz", Objects.isNull(testQualifierNameInjectz), testQualifierNameInjectz);
    }
}

结果:
在这里插入图片描述
@Qualifier 作用:指定注入bean的名称

通过@Qualifier(“testQualifierNameInjecty”) 指定只能注入名称为"testQualifierNameInjecty"的bean,如果这个名称的bean不存在则无法注入,而且找不到bean时不会自动使用byType注入,直接提示报错

同时可以解决存在多个相同类型的bean时启动不成功的问题。没有加@Qualifier注解的话会提示:
在这里插入图片描述

@Autowired通过@Primary解决多个同类型bean的问题

在上面的一个小节结尾疏说到可以通过@Qualifier解决多个同类型bean,接下来介绍一下通过@Primary解决这个问题。
首先,要说明一点的是,@Primary如果同时使用在同一个类型的多个Bean上,就会出现下面的报错,所以基本上只能在同一个类型的一个bean上使用

No qualifying bean of type
‘XXX’ available: more than
one ‘primary’ bean found among candidates: [XXX]

config:

@Configuration
public class CustomConfig {
    /**
     * 测试@Primary注入,优先使用testPrimaryInjecty
     */
    @Bean
    public TestPrimaryInject testPrimaryInjectx() {
        return new TestPrimaryInject("testPrimaryInject x");
    }

    @Bean
    @Primary
    public TestPrimaryInject testPrimaryInjecty() {
        return new TestPrimaryInject("testPrimaryInject y");
    }

测试类:

@Service
public class AutowiredTestService {
     /**
     * 测试@Primary注入
     *
     * @date 2022/9/29
     */
    @Autowired
    private TestPrimaryInject testPrimaryInject;

    public void printObjectPrimary() {
        //这里注入的是testPrimaryInjecty
        log.info("对象:{},是否为空:{},对象信息:{}", "testPrimaryInject", Objects.isNull(testPrimaryInject), testPrimaryInject);
    }
}

结果:
在这里插入图片描述

@Primary作用:当我们使用自动配置的方式装配Bean时,如果这个Bean有多个候选者,假如其中一个候选者具有@Primary注解修饰,该候选者会被选中,作为自动配置的值。

我通过@Primary指定优先使用testPrimaryInjecty这个bean,所以AutowiredTestService 能够注入成功,且注入的是testPrimaryInjecty这个bean。

@Autowired应用到方法上

被@Autowried注解的方法,是不需要我们调用,Spring会自己调用。
这里的方法同时演示了静态方法和非静态方法,setter方法的调用。

注册Spring对象:

@Configuration
public class CustomConfig {
    @Bean
    public TestAutowiredFirst testAutowiredFirst() {
        return new TestAutowiredFirst("对象1");
    }
}

测试:

/**
 * @date 2022/9/28
 */
@Slf4j
@Service
public class TestMethodInjectService {
    private TestSetterMethodInject testSetterMethodInject;


    /**
     * 演示setter方法注入
     *
     * @param testSetterMethodInject
     */
    @Autowired
    public void setTestAutowiredFirst1(TestSetterMethodInject testSetterMethodInject) {
        this.testSetterMethodInject = testSetterMethodInject;
    }

    /**
     * 演示普通方法注入
     *
     * @param testNormalMethodInject
     */
    @Autowired
    private void tsstGetObjectByParam(TestNormalMethodInject testNormalMethodInject) {
        log.info("普通方法注入对象,对象:{},是否为空:{},对象信息:{}", "testNormalMethodInject", Objects.isNull(testNormalMethodInject), testNormalMethodInject);
    }

    /**
     * 无效,提示:Autowired annotation is not supported on static methods
     * @param testStaticMethodInject
     */
    @Autowired
    public static void testStaticObject(TestStaticMethodInject testStaticMethodInject) {
        log.info("静态方法注入对象,对象:{},是否为空:{},对象信息:{}", "testStaticMethodInject", Objects.isNull(testStaticMethodInject), testStaticMethodInject);
    }


    public void printObject() {
        log.info("对象:{},是否为空:{},对象信息:{}", "testSetterMethodInject", Objects.isNull(testSetterMethodInject), testSetterMethodInject);
    }
}

结果:
在这里插入图片描述

在这里插入图片描述

根据提示,@Autowired其实是不能应用到static方法上的。

@Autowired应用到构造器上

配置类:

    /**
     * 测试构造器注入
     *
     * @return
     */
    @Bean
    public TestConstructInject testConstructInject() {
        return new TestConstructInject("Construct对象1");
    }

测试程序:

/**
 * 测试@Autowired构造器注入
 *
 * @date 2022/9/28
 */
@Slf4j
@Service
public class TestConstructInjectService {
    private TestConstructInject testConstructInject;

    /**
     * 测试构造器注入
     *
     * @return
     */
    @Autowired
    public TestConstructInjectService(TestConstructInject testConstructInject) {
        this.testConstructInject = testConstructInject;
    }


    public void printObject() {
        log.info("对象:{},是否为空:{},对象信息:{}", "testAutowiredFirst", Objects.isNull(testConstructInject), testConstructInject);
    }
}

结果:
在这里插入图片描述

@Autowired应用到构造器参数上

配置类:

    /**
     * 测试构造器参数注入
     *
     * @return
     */
    @Bean
    public TestConstructParamInject testConstructParamInject() {
        return new TestConstructParamInject("ConstructParam对象1");
    }

测试程序:

/**
 * 测试@Autowired构造器参数注入
 *
 * @date 2022/9/28
 */
@Slf4j
@Service
public class TestContructParamInjectService {
    private TestConstructParamInject testConstructParamInject;

	/**
	 * 注解在这里也是可以的
	 */
    public TestContructParamInjectService(@Autowired TestConstructParamInject testConstructParamInject) {
        this.testConstructParamInject = testConstructParamInject;
    }

    public void printObject() {
        log.info("对象:{},是否为空:{},对象信息:{}", "testConstructParamInject", Objects.isNull(testConstructParamInject), testConstructParamInject);
    }
}

结果:
在这里插入图片描述

@Autowired应用到方法参数上

配置类:

	/**
     * 测试方法参数注入
     *
     * @return
     */
    @Bean
    public TestMethodParamInject testMethodParamInject() {
        return new TestMethodParamInject("方法参数对象1");
    }
	
    @Bean
    public TestMethodParamInject testMethodParamInject2(@Autowired TestMethodParamInject testMethodParamInject) {
        log.info("配置方法注入对象,对象:{},是否为空:{},对象信息:{}", "testMethodParamInject", Objects.isNull(testMethodParamInject), testMethodParamInject);
        return testMethodParamInject;
    }

    @Bean
    public TestMethodParamInject testMethodParamInject3(TestMethodParamInject testMethodParamInject) {
        log.info("配置方法【不加@Autowired】注入对象,对象:{},是否为空:{},对象信息:{}", "testMethodParamInject", Objects.isNull(testMethodParamInject), testMethodParamInject);
        return testMethodParamInject;
    }

测试程序:

@Slf4j
@Service
public class TestMethodParamInjectService {
    private TestMethodParamInject testMethodParamInject;

    /**
     * 演示setter方法注入
     *
     * @param testMethodParamInject
     */
    public void setTestMethodParamInject(@Autowired TestMethodParamInject testMethodParamInject) {
        this.testMethodParamInject = testMethodParamInject;
    }

    /**
     * 演示普通方法注入
     *
     * @param testMethodParamInject
     */
    private void tsstGetObjectByParam(@Autowired TestMethodParamInject testMethodParamInject) {
        log.info("普通方法注入对象,对象:{},是否为空:{},对象信息:{}", "testMethodParamInject", Objects.isNull(testMethodParamInject), testMethodParamInject);
    }

    /**
     * 无效,提示:Autowired annotation is not supported on static methods
     *
     * @param testMethodParamInject
     */
    public static void testStaticObject(@Autowired TestMethodParamInject testMethodParamInject) {
        log.info("静态方法注入对象,对象:{},是否为空:{},对象信息:{}", "testMethodParamInject", Objects.isNull(testMethodParamInject), testMethodParamInject);
    }


    public void printObject() {
        log.info("对象:{},是否为空:{},对象信息:{}", "testMethodParamInject", Objects.isNull(testMethodParamInject), testMethodParamInject);
    }
}

结果:
在这里插入图片描述
在这里插入图片描述

这里通过结果可以知道,当@Autowired标记在方法参数时,
1)静态方法无效
2)非静态方法也无效
3)配置方法加不加无所谓,反正都会注入

我通过网上资料查询,@Autowired对于普通方法参数确实是无效的,但是在测试方法里面是有效。

    @Test
    void testMethodParam(@Autowired TestMethodParamInject TestMethodParamInject) {
        log.info("测试方法对象:{},是否为空:{},对象信息:{}", "TestMethodParamInject", Objects.isNull(TestMethodParamInject), TestMethodParamInject);
    }

结果:
在这里插入图片描述

演示@Resource 所有装配方式

@Resource 字段注入

配置类:

/**
 * 新注册两个bean,如果单纯使用@Autowired,那么就会报错
 */
@Configuration
public class CustomConfig {
     /**
     * 测试@Resource字段注入
     *
     * @return
     */
    @Bean
    public TestResourceFieldInject testResourceFieldInject() {
        return new TestResourceFieldInject("Resource对象1");
    }


    /**
     * 测试@Resource字段注入(byName)
     *
     * @return
     */
    @Bean
    public TestResourceByNameInject testResourceByNameInjectX() {
        return new TestResourceByNameInject("testResourceByNameInject对象X");
    }


    @Bean
    public TestResourceByNameInject testResourceByNameInjectY() {
        return new TestResourceByNameInject("testResourceByNameInject对象Y");
    }
}

测试类:

@Slf4j
@Service
public class TestResourceFieldInjectService {
    /**
     * 演示@Resource字段注入(byName),可以通过name属性换个名称
     */
     @Resource
    //@Resource(name="testResourceByNameInjectX")
    private TestResourceByNameInject testResourceByNameInjectX;

    /**
     * 演示@Resource字段注入(byType)
     */
    @Resource
    private TestResourceFieldInject testResourceAnotherName;


    public void printObject() {
        //两个bean,基于变量名,注入了testResourceByNameInjectX
        log.info("对象:{},是否为空:{},对象信息:{}", "testResourceByNameInjectX", Objects.isNull(testResourceByNameInjectX), testResourceByNameInjectX);
        //变量名与bean名(testResourceFieldInject)不同,但是此时该类型只有一个bean,所以可以使用byType注入
        log.info("对象:{},是否为空:{},对象信息:{}", "testResourceAnotherName", Objects.isNull(testResourceAnotherName), testResourceAnotherName);
    }
}

结果:
在这里插入图片描述
@Resource:默认使用byName注入,如果成员变量名不同于bean名称且同类型的bean只有一个实例,则会使用byType注入

@Resource对于多个同类型且无法匹配名称的Bean

如果存在多个同类型的bean,且名字都没办法匹配成员变量名,则会提示报错,无法启动。

@Configuration
public class CustomConfig {
    /**
     * 测试@Resource字段注入(byName)
     *
     * @return
     */
    @Bean
    public TestResourceByNameInject testResourceByNameInjectX() {
        return new TestResourceByNameInject("testResourceByNameInject对象X");
    }


    @Bean
    public TestResourceByNameInject testResourceByNameInjectY() {
        return new TestResourceByNameInject("testResourceByNameInject对象Y");
    }
 }

测试类:

@Slf4j
@Service
public class TestResourceFieldInjectService {
   
    /**
     * 演示@Resource字段注入失败,存在多实例
     */
    @Resource
    private TestResourceByNameInject testResourceByNameInjectZ;
}

结果
在这里插入图片描述

@Resource应用到方法上

配置类:

    @Bean
    public TestResourceSetterMethodInject testResourceSetterMethodInject() {
        return new TestResourceSetterMethodInject("ResourceSetterMethod对象1");
    }

    @Bean
    public TestResourceNormalMethodInject testResourceNormalMethodInject() {
        return new TestResourceNormalMethodInject("ResourceNormalMethod对象1");
    }

    @Bean
    public TestResourceStaticMethodInject testResourceStaticMethodInject() {
        return new TestResourceStaticMethodInject("ResourceStaticMethod对象1");
    }

测试程序:

/**
 * 测试@Resource方法注入
 *
 * @author XBird
 * @date 2022/9/29
 **/
@Slf4j
@Service
public class TestResourceMethodInjectService {
    private TestResourceSetterMethodInject testResourceSetterMethodInject;


    /**
     * 演示setter方法注入
     *
     * @param testResourceSetterMethodInject
     */
    @Resource
    public void setTestResourceSetterMethodInject(TestResourceSetterMethodInject testResourceSetterMethodInject) {
        this.testResourceSetterMethodInject = testResourceSetterMethodInject;
    }

    /**
     * 演示普通方法注入
     *
     * @param testResourceNormalMethodInject
     */
    @Resource
    private void tsstGetObjectByParam(TestResourceNormalMethodInject testResourceNormalMethodInject) {
        log.info("@Resource普通方法注入对象,对象:{},是否为空:{},对象信息:{}", "testResourceNormalMethodInject", Objects.isNull(testResourceNormalMethodInject), testResourceNormalMethodInject);
    }

    /**
     * 无效,提示:@Resource annotation is not supported on static methods,且导致应用启动失败
     *
     * @param testResourceStaticMethodInject
     */
//    @Resource
    public static void testStaticObject(TestResourceStaticMethodInject testResourceStaticMethodInject) {
        log.info("@Resource静态方法注入对象,对象:{},是否为空:{},对象信息:{}", "testResourceStaticMethodInject", Objects.isNull(testResourceStaticMethodInject), testResourceStaticMethodInject);
    }


    public void printObject() {
        //两个bean,基于变量名,注入了testResourceByNameInjectX
        log.info("对象:{},是否为空:{},对象信息:{}", "testResourceSetterMethodInject", Objects.isNull(testResourceSetterMethodInject), testResourceSetterMethodInject);
    }
}

结果:
根据测试,@Resource并不能用在静态方法上,且会导致应用失败,只能应用在普通方法和setter方法上。
在这里插入图片描述
在这里插入图片描述

@AutowiredVS@Resource

其实,简单来说,他们的功能都是依赖注入。
不同点在于:

  1. 提供方
    @Autowired 是由Spring提供的,包名是:org.springframework.beans.factory.annotation
    @Resource 是由Java提供的,包名是:javax.annotation

  2. 依赖识别方式
    @Autowired 默认是以byType方式,可以使用@Qualifier指定bean名称,如果找不到Bean不会自动使用byName方式。
    @Resource 默认是以byName方式,当byName方式无法匹配时,会使用byType方式。(仅适用于仅注册了一个Bean对象的类型)

  3. 适用对象
    @Autowired 可以使用在方法,方法参数,构造器,构造器参数,字段上
    @Resource只能使用在方法,字段上(经过实测,无法注解在构造器和参数上)

  4. 强依赖型
    @Autowired和@Resource都是具有强依赖性,也就是必须要有这个bean才能启动,不过@Autowired可以设置属性required=false变成非强制注入

各种注入方式优缺点

构造器注入:强依赖性(即必须使用此依赖),不变性(各依赖不会经常变动)
Setter注入:可选(没有此依赖也可以工作),可变(依赖会经常变动)
Field注入:大多数情况下尽量少使用字段注入,一定要使用的话, @Resource相对@Autowired对IoC容器的耦合更低,因为如果脱离了Spring容器的话,就不得不提供setter方法或者构造器方法去重写设置成员变量的逻辑,改动大。

Field注入的缺点

  1. 不能像构造器那样注入不可变的对象(程序会提示:java: 变量 XXX未在默认构造器中初始化,@Autowired和@Reousrce都是)

  2. 依赖对外部不可见,外界可以看到构造器和setter,但无法看到私有字段,自然无法了解所需依赖

  3. 会导致组件与IoC容器紧耦合(这是最重要的原因,离开了IoC容器去使用组件,在注入依赖时就会十分困难)

  4. 导致单元测试也必须使用IoC容器,原因同上

  5. 依赖过多时不够明显,比如我需要10个依赖,用构造器注入就会显得庞大,这时候应该考虑一下此组件是不是违反了单一职责原则

为什么idea不建议使用@Autowired

因为@Autowired太方便了,有了@Autowired基本不需要提供setter和构造器,省去了很多代码,但是也造成了业务程序与Spring框架的高度耦合,如果脱离Spring框架去使用其他的IOC框架,就不得不改动原来的业务程序。
而 @Resource是JSR-250提供的,它是Java标准,我们使用的IoC容器应当去兼容它,这样即使更换容器,也可以正常工作。

而且,@Autowired不能应用到final修饰的成员,因为final类型的变量在调用class的构造函数的这个过程当中就得初始化完成,基于字段的注入是使用set形式注入的

Spring5其实更加推荐使用构造器注入,原因是构造器注入可以保证依赖不变,因为依赖都需要使用final修饰。
构造器注入的使用条件:

  1. 使用private final修饰成员变量
  2. 提供包含private final成员变量在内的构造器(用Lombok的注解也可以)
  3. 不能和@Autowired的注解方式一起使用,只能使用其中一种
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:27:04  更:2022-10-08 20:31:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年4日历 -2025/4/19 4:31:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码