Bean 注入失败问题汇总

6260546388dc13171e231e26a2ccaa24.jpeg

程序员的成长之路

互联网/程序员/技术/资料共享 

关注

阅读本文大概需要 5 分钟。

来自:blog.csdn.net/qq_51439643/article/details/124781526

目录

  • 错误案例

    • 常见情况一:bean 没有被扫描

    • 常见情况二:多模块架构 bean 没有被扫描

    • 常见情况三:使用@Qualifier 或 @Resource 注入时指定的 name 不存在

    • 常见情况四:在拦截器或过滤器或监听器中注入 bean

  • 使用 IDEA 工具查看 IOC 容器

错误案例

常见情况一:bean 没有被扫描

常见问题

SpringBoot项目启动时,会默认自动扫描启动类所在的包以及子包下的Bean。

例如下面的这种情况:

e18116a3e26d4a0d83d9eaed148a95c8.png

项目希望在 com.training 包下放置主函数与服务代码,在 com.utils 包下放置一些配置与工具类。

因为启动类所在的包是:com.training,所以在com.training包与其子包下的 Bean 会被默认自动扫描加入到 Spring 容器中。

而 com.utils 包不包含在默认扫描的包之内,就算是将 bean1 对象使用注解尝试注入容器,Spring 也不会扫描到该类,因此这是大部分 bean 注入失败的原因。

解决方法

Spring 默认扫描的是启动类所在的包,为了将 com.utils 包加入到扫描中,可以使用 @ComponentScan 指定额外的扫描包,配置如下:

package com.training;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
// 将com.utils 包加入到 Spring 扫描范围中
@ComponentScan(basePackages = {"com.training","com.utils"})
@MapperScan(basePackages = "com.training.dao")
public class MusicMainClass {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(MusicMainClass.class, args);
}
}

需要注意的是:指定 @ComponetScan 后默认的扫描位置会发生改变,因此添加新的扫描包后要确保启动类所在的包也能被扫描到!

不仅仅可以使用 @ComponentScan 进行配置,@SpringBootApplication 中的 scanBasePackages 属性指定也能起到同样的作用,配置方式如下:

package com.training;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

// 使用 scanBasePackages 属性同样能够实现
@SpringBootApplication(scanBasePackages = {"com.training","com.utils"})
@MapperScan(basePackages = "com.training.dao")
public class MusicMainClass {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(MusicMainClass.class, args);
}
}

常见情况二:多模块架构 bean 没有被扫描

常见问题

多模块架构中 bean 无法注入常见问题与情况一相同,都是忽略了 SpringBoot 默认扫描启动类所在包下的 bean。

例如如下的架构:

78025e520b86e9d1e2ae0164cfcec36f.png

因为子模块中包路径为 com.common.* ,而启动类所在的包路径为:com.training.* ,因此子模块中的 bean 无法被 Spring 自动扫描到。

解决方法

与情况一同理,指定 @ComponentScan 或 ScanbasePackages 属性,这里不过多赘述。

常见情况三:使用@Qualifier 或 @Resource 注入时指定的 name 不存在

bean名称默认规则

使用 Spring 的注解 @Component@Repository@Service@Controller 去把一个类配置为bean时,如果不指定bean的名称,那么 bean 的名称的默认规则是:

  • 类名的首字母小写,例如:类名称 UserDao ,那么默认的bean名称 userDao

  • 如果类名前两个(或两个以上)连续的字母都是大写,那么默认的bean名称与类名一样,例如:类名称 MIXDao ,那么默认的bean名称 MIXDao

使用 SpringBoot 的注解 @Bean + @Configuration 注入 bean 时,如果不指定 bean 的名称,那么 bean 的名称默认规则是:bean 名称与注入的方法名同名

简述@Qualifer注解

@Autowired 注解能够自动注入同类型的 bean,但如果与该属性同类型的 bean 不止一个存在时,Spring 无法判断具体注入哪个 bean ,@Qualifer 注解就能够解决这样的歧义。

@Qualifer 能够指定 value 字段指定需要注入 bean 的名称,通过指定名称来指定 Spring 到底应该注入哪一个 bean ,这样就能消除歧义,例如下面的例子:

public class Teacher{    
@Autowired    
private Student student;
}

定义一个对象 Teacher 其中包含一个 Student 字段的对象

<bean id="teacher" class="com.test.Teacher"/>

# 下面有两个相同类型不同 name 的 Student bean
<bean id="student1"class="com.test.Student">
<property name="name"value="zhangsan"/>
</bean>

<bean id="student2"class="com.test.Student">
<property name="name"value="lisi"/>
</bean>

在 xml 文件中配置上述 bean ,此时如果直接注入 Student 那么程序就会报错,因为 @Autowired 注解产生了歧义,因此需要使用 @Qualifer 注解消除歧义:

public class Teacher{    
@Qualifer(value = "student1")
@Autowired    
private Student student;
}

简述 @Resource 注解

@Resource 注解默认通过 名称 进行注入,名称可以通过 name 属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。

但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

常见问题

知道了 bean 的默认命名规则与指定名称的两个注入注解后,可以试着查看注入的 bean 是否出现歧义,如果出现可以使用 @Qualifer 注解进行限定。

同时使用上述的两个注解之前,需要确保 IOC 容器中存在指定的 bean ,如果没有相应的 bean 也会出现注入失败的情况!

常见情况四:在拦截器或过滤器或监听器中注入 bean

叨叨两句

这两天在写项目,这个问题也是困扰了我很久很久(十几个小时至少)。

在项目中需要集成 SpringBoot + SpringSecurity 做认证授权处理,确定注入的 bean 能够被 Spring 扫描到,同时在 Controller、Service 层的注入也没有问题,但是在用户授权过滤器 UsernamePasswordAuthenticationFilter 中死活就是注入不了 bean。

开始查阅了大量的博客资料也根本没有往过滤器这方面来思考,在不断地折磨后最终还是找到了问题的所在。

常见问题

在 WebMVC 框架中,各个元素的加载是有一定顺序的,这个顺序按照:

context-param–>listener–>filter–>servlet --> … -> Bean 的实例化

而拦截器是在Spring MVC中配置的,如果从整个项目中看,一个servlet请求的执行过程就变成了这样:

context-param–>listener–>filter–>servlet–>interceptor(指的是拦截器) --> … -> Bean 的实例化

因此上述元素中注入 bean 时,bean 对象的实例化还没有开始,因此注入的值永远为 null 值!

解决方法

上述对象时,使用实例化的方式配置或是将需要注入的对象注入配置类中,在配置类中使用构造函数实例化对象,例如下面的方式:

使用构造函数:

14011c930577a38e9b7839dab295b015.png

将需要注入的对象在配置类中注入,并将其传入构造函数参数进行实例化:

19975462e54ecdc12c5620cde9153063.png

使用 IDEA 工具查看 IOC 容器

众所周知 idea 作为 java 程序员的一大利器,其功能相当的强大,今天也是在苦恼于 bean 注入失败原因时,发现了IDEA支持查看 IOC 容器的功能,有了这一功能以后再遇上 bean 注入失败时就能够更快地找到失败原因了!

使用

当启动 SpringBoot 项目后点击下方的 Run 按钮(Debug 启动方式点击 Debug 按钮):

05305490bae69d900c010ec15a9aed03.png

点击 Console 栏上方的 Actuator :

8c00893faf2c2dcde25161616a66fd31.png

进入下面的页面后点击 Beans 与 Application :

e2503d1ef06e552b9449825f3c2ac114.png

可以看到右方出现了 IOC 容器中的所有 bean 数据信息:

056da9b20a3a868b34d0d4450ecc8cb7.png

有了这一利器就再也不怕 bean 无法注入的问题了!

<END>

推荐阅读:

Jenkins+Docker 实现一键自动化部署项目!步骤齐全,少走坑路

终于搞懂了 @Configuration 和 @Component 的区别

互联网初中高级大厂面试题(9个G)
内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈!
⬇戳阅读原文领取!                                  朕已阅
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: @Autowired循环依赖问题是指在使用@Autowired注解进行属性注入时,如果存在循环依赖关系,会导致注入失败。解决循环依赖问题的时机是在Bean的创建过程中。当Spring容器创建Bean时,会先创建Bean的实例,然后再进行属性注入。如果发现存在循环依赖关系,Spring会将正在创建的Bean放入一个缓存中,然后继续创建其他Bean。当其他Bean创建完成后,Spring会再次尝试注入之前缓存的Bean,从而解决循环依赖问题。\[2\] 在使用@Autowired注解时,可以通过setter方法进行注入。首先定义一个成员变量,然后使用@Autowired注解标注setter方法,将需要注入Bean作为参数传入。这样,在Spring容器创建Bean时,会自动调用setter方法进行注入。\[2\] 另外,循环依赖问题也可以通过使用@Resource注解来解决。@Resource注解默认通过byname的方式实现注入,如果找不到对应的名字,则通过byType实现。如果两种方式都找不到,就会报错。因此,可以使用@Resource注解来解决循环依赖问题。\[1\] 总结起来,循环依赖问题可以通过在Bean的创建过程中解决,使用@Autowired注解的setter方法或@Resource注解来实现属性注入,从而解决循环依赖问题。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* *3* [SpringBean自注入问题之@Autowired进来的Bean为null 循环依赖大致处理过程](https://blog.csdn.net/Be_insighted/article/details/121526557)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值