关于@Autowired注入依赖失败的问题及解决
作者:夜雨落花 发布时间:2023-11-29 00:50:42
@Autowired注入依赖失败的问题
1、现象描述
在Spring Boot项目中使用@Autowired注解,程序启动时发现服务启动失败,提示:
Description:
Field metrics in com.be.fallback.servlet.FallbackServlet required a bean of type 'com.be.fallback.metrics.FallbackMetrics' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.be.fallback.metrics.FallbackMetrics' in your configuration.
2、问题分析
这里的错误原因很好分析。结合报错信息及代码,报错处的代码为FallbackMetrics注解了@Autowired进行依赖注入,但是没有找到可以被用来注入的实例。即Spring Boot获取FallbackMetrics的实例失败。
3、解决方案
根据分析,需要检查可能导致Spring Boot依赖注入失败的因素。
1.检查扫描路径。
扫描路径是由@ComponentScan来指定的,默认为标注类当前包及当前包的子包。
也就是说,标注了@ComponentScan的启动类放在com.be.fallback包下面,只会扫描com.be.fallback包中的类,以及com.be.fallback.servlet、com.be.fallback.util等子包中的类,对于com.be.service等包中的类是不会扫描的。
注意事项一:很多人没有使用@ComponentScan,但是使用了@SpringBootApplication。@SpringBootApplication是通过内部封装@ComponentScan注解来实现实例扫描的,所以使用@SpringBootApplication也是一样的。
注意事项二:也可以通过为@ComponentScan或@SpringBootApplication注解指定参数来修改扫描路劲,示例:
// 如果使用@ComponentScan注解:
@ComponentScan(basePackages = "com.be.fallback")
// 如果使用@SpringBootApplication注解:
@SpringBootApplication(scanBasePackages = "com.be.fallback")
2.检查实例注册。
检查想要使用@Autowired注解自动注入依赖的类,是否标注了用来注册给Spring Boot的注解。这些注解包括@Component,@Service,@Repository,@Controller等。
3.其他问题。
如果上述步骤检查完成,服务启动又没有产生其他异常,这时候基本上已经排查代码的问题。这时候需要检查依赖、开发环境等是否有问题。检查依赖需要了解自己需要哪些依赖,看是否配置齐全;检查开发环境,可以通过将代码拷贝到其他机器上执行来判断。
@Autowired依赖注入为啥不推荐了
这几天更新升级了一下java编码神器IDEA,升级完进行日常开发,可能是以前用的IDEA版本比较老旧,升级之后发现之前的日常写法有了个warning提醒。来看图:
如上图,这就奇怪了,我们经常写的业务层就是service接口层和对应的实现类层进行属性注入的时候都是采用注解进行注入的。这也是springIOC给提供的比较方便的地方。我使用IDEA提供的自动修复提示修复了之后变成采用构造函数的形式进行注入了。
但是多年面向Spring开发的经验告诉我,使用@Autowired注解进行依赖注入,肯定是没有问题的。但是我的代码洁癖不允许我这么不明不白的留一个警告在这里。所以,带着我的洁癖,和我的好奇心,我开始研究起了这个警告。
警告内容
我们简单翻译一下自动提示的是啥意思:
不建议直接在字段上进行依赖注入。
Spring 开发团队建议:在Java Bean中永远使用构造方法进行依赖注入。对于必须的依赖,永远使用断言来确认。
我们说明上面的问题之前先回顾几个spring相关的问题:
依赖注入的方式
Spring 有三种依赖注入的方式
1.基于属性的注入
这种注入方式就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是我平常开发中看的最多也是最熟悉的一种方式。
@Autowired
PushTaskService pushTaskService;
2.基于setter方法的注入
通过对应变量的setXXX()方法以及在方法上面使用注解,来完成依赖注入。比如:
private static TaskGroupTemplateRepository taskGroupTemplateRepository;
private static TaskGroupService taskGroupService;
@Autowired
public void setTaskGroupTemplateRepository(TaskGroupTemplateRepository taskGroupTemplateRepository,TaskGroupService taskGroupService){
ExcelListener2.taskGroupTemplateRepository = taskGroupTemplateRepository;
ExcelListener2.taskGroupService = taskGroupService;
}
说明:在 Spring 4.5 及更高的版本中,setXXX 上面的 @Autowired 注解是可以不写的。
3.基于构造方法的注入
将各个必需的依赖全部放在带有注解构造方法的参数中,并在构造方法中完成对应变量的初始化,这种方式,就是基于构造方法的注入。比如:
@Autowired
public ExcelListener(@Qualifier("taskGroupService") TaskGroupService taskGroupService) {
this.taskGroupService = taskGroupService;
}
@Autowired是干啥的
我们一般开发需要注入属性的时候都会使用的这个注解@Autowired,跟这个注解类似的还有2个,@Resource, @Inject。Spring 支持使用@Autowired, @Resource, @Inject 三个注解进行依赖注入。我们先看一下有啥区别:
@Autowired为Spring框架提供的注解,可以理解是Spring的亲儿子。这里先给出一个示例代码
public interface IndexService {
void sayHello();
}
@Service
public class IndexServiceImpl implements IndexService {
@Override
public void sayHello() {
System.out.println("hello, this is IndexServiceImpl");
}
}
@Service
public class IndexServiceImpl2 implements IndexService {
@Override
public void sayHello() {
System.out.println("hello, this is IndexServiceImpl2");
}
}
测试方法
@SpringBootTest
public class Stest {
@Autowired
// @Qualifier("indexServiceImpl2")
IndexService indexService;
@Test
void gooo() {
Assertions.assertNotNull(indexService);
indexService.sayHello();
}
}
按照type在上下文中查找匹配,查找type为IndexService的bean
如果有多个bean,则按照name进行匹配
如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配,查找name为indexServiceImpl2的bean
如果没有,则按照变量名进行匹配。查找name为indexService的bean
匹配不到,则报错。(@Autowired(required=false),如果设置required为false(默认为true),则注入失败时不会抛出异常)
@Inject是干啥的
在Spring 的环境下,@Inject和@Autowired 是相同的,因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor这个后置处理器来处理的。
这两个的区别,首先@Inject是Java EE包里的,在SE环境需要单独引入。另一个区别在于@Autowired可以设置required=false而@Inject并没有这个属性。也有的说@Inject是spring的干儿子。
@Resource是干啥的
@Resource是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理,其中就包括@Resource。
这个@Resource有2个属性name和type。在spring中name属性定义为bean的名字,type这是bean的类型。
如果属性上加@Resource注解那么他的注入流程是:
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常。
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。
上面的内容都了解了之后我们接下来看为啥IDEA会有个warning提醒。IDEA 提示 Field injection is not recommended。warning提醒的注入方式就是第一种使用属性注解的方式进行注入。
属性注入优点
代码看起来很简单,通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了方便开发人员进行代码的编写。
属性注入可能出现的问题
问题1
基于 field 的注入,虽然不是绝对禁止使用,但是它可能会带来一些隐含的问题。来我们举个例子:
@Autowired
private Person person;
private String company;
public UserServiceImpl(){
this.company = person.getCompany();
}
初看起来好像没有什么问题,Person 类会被作为一个依赖被注入到当前类中,同时这个类的 company 属性将在初始化时通过person.getCompany() 方法来获得值。我们尝试运行一下就会发现报错了。其实类似这个问题有人在stackoverflow上提问过,点我跳转。
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...]: Constructor threw exception; nested exception is java.lang.NullPointerException
在执行this.company = person.getCompany();这段代码的时候报了空指针。
出现这个问题的原因是,Java 在初始化一个类时,是按照静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 -> @Autowired 的顺序。所以在执行这个类的构造方法时,person 对象尚未被注入,它的值还是 null。
问题2
使用这种基于 field 注入的方式,添加依赖是很简单的,就算你的类中有十几个依赖你可能都觉得没有什么问题,如果你一个类注入非常多的其它的对象,拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则。顺便我看了一下我们现在的业务代码这个问题在我们的项目代码中真的很常见。
问题3
这种注入形式就会造成你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化。也就是类和依赖容器强耦合,不能在容器外使用。
spring建议
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.
其实大概的意思就是2句话
强制依赖就用构造器方式
可选、可变的依赖就用setter注入
spring对采用构造方法注入的说明
Spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service),在被调用时可以保证它们都完全准备好了。与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码结构问题,这个类可能承担了过多的责任。
spring对采用setter方法注入的说明
基于 setter 的注入,则只应该被用于注入非必需的依赖,同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖,那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入。
来源:https://blog.csdn.net/weixin_38569499/article/details/86617384
猜你喜欢
- 首先,这两者是完全不同的概念,绝对不能混为一谈。1.什么是Java内存模型?Java内存模型是Java语言在多线程并 * 况下对于共享变量读写
- Servlet3.0的出现是servlet史上最大的变革,其中的许多新特性大大的简化了web应用的开发,为广大劳苦的程序员减轻了压力,提高了
- mybatis的原身是ibatis,现在已经脱离了apache基金会,新官网是http://www.mybatis.org/。在研究Myba
- Java代码package com.zzx.controller;import com.zzx.model.User;import org.
- DATAXDataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、Oracle、SqlServer、Postg
- 自定义 webflux 容器配置配置代码@Componentpublic class ContainerConfig extends Rea
- 双向顺序队列ArrayDeque和双向链式队列LinkedList,JDK已经包含,在此略。ArrayDeque包括顺序栈和顺序队列,Lin
- Springboot自带定时任务实现动态配置Cron参数同学们,我今天分享一下SpringBoot动态配置Cron参数。场景是这样子的:后台
- 这篇文章主要介绍了SpringBoot下如何实现支付宝接口的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 异常与错误:异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通
- 本文实例为大家分享了java实现猜数字游戏的具体代码,供大家参考,具体内容如下随机生成0~100的数字,通过控制台输入猜测数字,输出进行提示
- SlidingDrawer效果想必大家也见到过,它就是1.5模拟器上进入应用程序列表的效果。下面是截图一、简介 SlidingDr
- 一、问题重现1.配置文件spring: #DataSource数据源 datasource: &nbs
- 一 前言redis在分布式应用十分广泛,本篇文章也是互联网面试的重点内容,读者至少需要知道为什么需要分布式锁,分布式锁的实现原理,分布式锁的
- 昨天写了一篇Redis布隆过滤器相关的命令的文章,今天来说一说springboot中如何简单在代码中使用布隆过滤器吧。目前市面上也有好几种实
- MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoD
- mybatis 查询返回Map<String,Object> 类型,平时没太注意怎么用,今天又遇到了总结记录一下,方便以后处理此
- 本Demo为练手小项目,主要是熟悉目前主流APP的架构模式.此项目中采用MVC设计模式,纯代码和少许XIB方式实现.主要实现了朋友圈功能和摇
- 项目介绍springboot搭建的访客管理系统,针对高端基地做严格把控来访人员信息管理,用户后端可以设置多个管理员帐号,给予不同部门的管理层
- 一般都在windows下开发的,现在部署到linux下:1,将项目达成war包(用eclipse,项目右键-->Export-->