通过一行代码深入解析SpringBoot组件扫描和自动配置

通过这篇文章,你可以学会:

SpringBoot组件扫描和自动配置的全流程 SpringBoot组件扫描的路径顺序是如何确定的 条件注解在上述流程中是怎么生效的 条件注解在使用时有什么坑

使用的spring-boot-starter-parent版本为:2.4.4。展示的代码做了简化,隐藏了业务相关信息,但不影响理解。

问题背景

最近在工作中遇到了一个问题,在SpringBoot项目启动的时候出现了报错,具体信息如下:

markdown复制代码***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'initJackson', defined in class path resource [com/peng/project/JacksonConfig.class], could not be registered. A bean with that name has already been defined in class path resource [com/peng/project/thirdparty/JacksonConfig.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true


很明显,有两个被 @Configuration 注解标记的 JacksonConfig,它们都有一个同名的 initJackson 方法,所以在启动的时候报错了。奇怪的是,其中一个initJackson方法是用了 @ConditionalOnMissingBean 注解标记的,而另一个却没有这个注解。按理说,@ConditionalOnMissingBean 标记的方法应该会被SpringBoot忽略掉,为什么会不生效呢?

具体的包和代码结构如下:

一共有两个模块,boot模块表示项目的启动模块,而thirdparty模块则表示引用的组件库,可能是自研的也可能是第三方提供的。在这两个模块中,有如下的类:

下面分别对boot模块和thirdparty模块的类进行介绍。

对于boot模块,ProjectMinimalsApplication 类表示SpringBoot的启动类,JacksonConfig类表示在boot模块中Jackson配置类,其代码如下:

java复制代码
// com.peng.project.ProjectMinimalsApplication
@SpringBootApplication(scanBasePackageClasses = ProjectMinimalsApplication.class, scanBasePackages = "com.peng.project.thirdparty")
public class ProjectMinimalsApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(ProjectMinimalsApplication.class)
                .beanNameGenerator(FullyQualifiedAnnotationBeanNameGenerator.INSTANCE)
                .run(args);
    }

}


// com.peng.project.JacksonConfig
@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer initJackson() {
        return builder -> {
            
        };
    }
}

对于thirdparty模块,FrameworkAutoConfiguration 类是组件库的自动配置类,JacksonConfig类是组件库中Jackson配置类,其代码如下:

java复制代码
// com.peng.project.thirdparty.FrameworkAutoConfiguration
@Import({
        JacksonConfig.class,
})
public class FrameworkAutoConfiguration {

}


// com.peng.project.thirdparty.JacksonConfig
@Configuration
public class JacksonConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public Jackson2ObjectMapperBuilderCustomizer initJackson() {
        return builder -> {
            
        };
    }
} 


FrameworkAutoConfiguration 是自动配置类,需要在thirdparty模块中配置META-INF/spring.factories 文件:

java复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
com.peng.project.thirdparty.FrameworkAutoConfiguration

代码就这么多,却在启动项目的时候,出现了上述错误。而且,当在META-INF/spring.factories文件增加thirdparty中的 JacksonConfig 配置时,项目却能正常启动了:

java复制代码
// 没有配置JacksonConfig,项目启动出错
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
com.peng.project.thirdparty.FrameworkAutoConfiguration


// 配置了JacksonConfig,项目正常启动
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\  
com.peng.project.thirdparty.JacksonConfig,\  
com.peng.project.thirdparty.FrameworkAutoConfiguration

这非常奇怪。thirdparty模块中的JacksonConfig 类明明已经被 @Configuration 注解标记了,为什么启动的时候 @ConditionalOnMissingBean 没有生效,而把 JacksonConfig 类加到自动配置类中的时候,却能正常启动?为解答这个问题,要先了解SpringBoot中被 @Configuration 注解的类 是如何被SpringBoot识别到的,也就是组件扫描和自动配置流程。

理解整体流程

熟悉SpringBoot的对Bean的生命周期都会有所了解。我们写的被@Component标记的或其派生注解(比如@Service、@Configuration等)标记的类,是怎么在SpringBoot运行时变为Bean对象的呢?如图所示:

在进入Bean的生命周期之前,需要找到这些被注解的类,将这些类定义解析成 BeanDefinition ,再根据BeanDefinition 信息构造出Bean对象。而将配置文件或配置类变为BeanDefinition 的方式有两种:组件扫描@ComponentScan 和自动配置 @EnableAutoConfiguration。

那这两种方式是如何把配置类解析成BeanDefinition的呢?这还需要经过一些步骤,如图所示:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值