Spring Boot自动配置的坑:为什么我的Bean没有被注入?

张开发
2026/4/7 13:12:03 15 分钟阅读

分享文章

Spring Boot自动配置的坑:为什么我的Bean没有被注入?
你有没有遇到过这种情况明明在配置类里写了Bean启动后却发现这个Bean消失了就像你精心准备的表白结果对方压根没看到。别慌今天我们就来扒一扒 Spring Boot自动配置的黑魔法。一、问题现场还原话说那天一位工作了3年的Java开发工程师小王兴冲冲地写了一个配置类Configuration public class MyConfig { Bean public DataSource dataSource() { System.out.println( 我的DataSource被创建了 ); return new DruidDataSource(); } }启动Spring Boot应用控制台确实打印了那句日志。但当他Autowired注入时问题来了Service public class UserService { Autowired private DataSource dataSource; // 注入的居然不是我创建的那个 }报错信息NoSuchBeanDefinitionException: No qualifying bean of type javax.sql.DataSource available小王心态崩了我的Bean去哪儿了二、原因剖析Spring Boot的”先入为主”法则要理解这个问题我们需要先了解Spring Boot的自动配置机制。Spring Boot就像一个控制欲很强的家长它默认情况下会先帮你配置好一切。2.1 自动配置的加载顺序Spring Boot的自动配置会在用户定义的Bean之前执行这就导致了一个经典的”覆盖问题”Spring Boot自动配置 → 用户Bean → 最终生效的Bean ↓ 可能覆盖了你的Bean2.2 条件装配的隐形规则Spring Boot使用了一系列Conditional注解来控制Bean的创建注解作用ConditionalOnClass当classpath中存在指定类时才生效ConditionalOnMissingBean当不存在指定Bean时才创建ConditionalOnProperty当配置文件中存在指定属性时才生效关键问题如果Spring Boot的自动配置先于你的Bean执行并且使用了ConditionalOnMissingBean那么你精心准备的Bean就会被”忽略”。2.3 实际问题场景DruidDataSource的自动配置类DruidDataSourceConfiguration使用ConditionalOnMissingBean(DataSource.class)如果它在扫描到你的Bean之前就已经创建了DataSource那么你的Bean自然就被”雪藏”了。三、解决方案夺回Bean的”控制权”方案一使用ConditionalOnMissingBean推荐Configuration public class MyDataSourceConfig { Bean ConditionalOnMissingBean(DataSource.class) // 只有当不存在DataSource时才创建 public DataSource dataSource() { return new DruidDataSource(); } }原理告诉Spring”只有当没有DataSource Bean的时候才用我这个”。方案二排除自动配置SpringBootApplication(exclude {DruidDataSourceAutoConfigure.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }适用场景当你确定要完全使用自己的配置时。方案三调整配置类的优先级Configuration AutoConfigureBefore(DataSourceAutoConfiguration.class) // 在自动配置之前加载 public class MyConfig { // ... }注意Spring Boot 2.x之后这种方式已经不太推荐使用了。四、排查工具debug日志当你遇到类似问题时可以开启debug模式来查看Bean的加载情况# application.yml debug: true或者在启动类中添加public static void main(String[] args) { SpringApplication.run(Application.class, args); } Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) { return beanFactory1 - { System.out.println( 当前所有Bean ); Arrays.stream(beanFactory1.getBeanDefinitionNames()) .forEach(System.out::println); }; }输出示例 当前所有Bean dataSource ← 这个是自动配置创建的 userService orderService myConfig.dataSource ← 你的Bean可能被命名为这样五、预防措施最佳实践5.1 理解Spring Boot的”约定优于配置”Spring Boot的设计理念是提供默认配置但允许覆盖。我们应该优先使用自动配置的Bean如Spring Boot已经配置好的DataSource只有在需要定制化时才覆盖使用ConditionalOnMissingBean来判断是否需要创建5.2 使用自定义配置属性# application.yml spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: 123456 druid: initial-size: 5 min-idle: 5而不是完全重写DataSource Bean。5.3 善用Primary注解Bean Primary // 标记为主要Bean注入时会优先使用 public DataSource myDataSource() { return new DruidDataSource(); }六、总结今天我们学到了要点说明问题本质自动配置Bean可能覆盖用户定义的Bean根本原因ConditionalOnMissingBean和加载顺序解决方案使用ConditionalOnMissingBean、排除自动配置、调整优先级排查工具debug模式、BeanFactoryPostProcessor最佳实践理解”约定优于配置”优先使用自动配置彩蛋小王最后终于解决了问题他的方法是——直接在配置类上添加了ConditionalOnMissingBean注解。他兴奋地给作者发消息“原来Spring Boot是个傲娇啊你不说不想要它就默认帮你准备一堆”今日互动你在Spring Boot自动配置中还踩过哪些坑有没有遇到过Bean被”偷梁换柱”的经历

更多文章