文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot自动配置失效怎么办

2023-06-25 16:17

关注

小编给大家分享一下SpringBoot自动配置失效怎么办,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

问题描述

下面是一个简单复现的代码片段,在你没有阅读完本文时,如果能做出正确的判断,那恭喜你可以节省阅读本文的时间了。

自动配置类:AutoTestConfiguration

@Configuration@EnableConfigurationProperties(TestProperties.class)@ConditionalOnProperty(prefix = "test", name = "enable")public class AutoTestConfiguration {    @Bean    @ConditionalOnMissingBean    public TestBean testBean(TestProperties properties){        System.out.println("this is executed.....");        return new TestBean();    }}

配置类 TestProperties

@ConfigurationProperties(prefix = "test")public class TestProperties {    private boolean enable = true;    public boolean isEnable() {        return enable;    }    public void setEnable(boolean enable) {        this.enable = enable;    }}

这两个类都在 root package 下,可以保证能够正常被 Spring 扫描到;那么问题是 TestBean 会不会被正常创建?当然这里的结论是不会。

可能有的同学会说你的 TestProperties 没有加 @Configuration 注解,Spring 不认识它,那真的是这样吗?很显然也不是。
在排查这个问题的过程中,也有遇到其他问题,也是之前没有遇到过的;即使 Spring 源码我看过很多遍,但是仍然会有一些边边角角让你意想不到的地方;下面就针对这个问题,慢慢来揭开它的面纱。

@EnableConfigurationProperties 注解行为

在之前的版本中,TestProperties 是有被 @Configuration 注解标注的

@Configuration // 可以被 spring 扫描@ConfigurationProperties(prefix = "test")public class TestProperties {    private boolean enable = true;    public boolean isEnable() {        return enable;    }    public void setEnable(boolean enable) {        this.enable = enable;    }}

常规的思路是,当 TestProperties 被扫描到之后,spring env 中就会有 test.enable=true 的 k-v 存在,当执行 AutoTestConfiguration 自动配置类刷新时,@ConditionalOnProperty(prefix = "test", name = "enable") 则会生效,进而 TestBean 被正常创建。

但事实并非如此,下面是对于此问题的验证

配置有效,AutoTestConfiguration 未刷新

两个点:

代码如下:

@SpringBootApplicationpublic class Application implements ApplicationListener<ApplicationReadyEvent> {   @Autowired   private ApplicationContext applicationContext;   public static void main(String[] args) {      SpringApplication.run(Application.class, args);   }   @Override   public void onApplicationEvent(ApplicationReadyEvent event) {      System.out.println(this.applicationContext.getEnvironment().getProperty("test.enable")  + "------");   }}

执行得到的结果是 AutoTestConfiguration#testBean 没有被执行,但test.enable 为 true。

这里说明 TestProperties 是有被刷新的,但是并没有对 @ConditionalOnProperty 起到作用,那么这里基本可以猜到是自动配置类上的 @ConditionalOnProperty 和 @EnableConfigurationProperties 的作用顺序问题。

在验证顺序问题之前,我尝试在 application.properties 中增加如下配置,re run 项目:

test.enable=true

到这里我得到了另一个 bean 冲突的问题。

prefix-type

异常提示如下:

Parameter 0 of method testBean in com.glmapper.bridge.boot.config.AutoTestConfiguration required a single bean, but 2 were found:
 - testProperties: defined in file [/Users/glmapper/Documents/project/exception-guides/target/classes/com/glmapper/bridge/boot/config/TestProperties.class]
 - test-com.glmapper.bridge.boot.config.TestProperties: defined in null

这里出现了 test-com.glmapper.bridge.boot.config.TestProperties 这个 name 的 bean。我尝试在代码中去检查是否有显示给定这个 bean 名字,但是没有找到,那只有一种可能,就是这个是被 spring 自己创建的。

这个过程在 spring 刷新阶段非常靠前,在排查这个问题时,还是耽误了一些时间,最后还是把问题定位一致前置到 beandefinitions 初始化才找到。

这里是 @EnableConfigurationProperties 注解的一个行为,依赖 EnableConfigurationPropertiesRegistrar,源码如下:

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {         .getQualifiedAttributeName(EnableConfigurationPropertiesRegistrar.class, "methodValidationExcludeFilter");   @Override   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {      registerInfrastructureBeans(registry);      registerMethodValidationExcludeFilter(registry);      ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);      // to register      getTypes(metadata).forEach(beanRegistrar::register);   }

通过代码比较容易看出,EnableConfigurationPropertiesRegistrar 会将目标 metadata 注册成 bean;继续 debug,找到了产生 prefix-type 格式 name 的 bean。

SpringBoot自动配置失效怎么办

下面是 getName 的具体代码

private String getName(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {    // 拿 prefix   String prefix = annotation.isPresent() ? annotation.getString("prefix") : "";   // prefix + "-" + 类全限定名    return (StringUtils.hasText(prefix) ? prefix + "-" + type.getName() : type.getName());}

到这里我们先明确一个问题:

如果你使用 @EnableConfigurationProperties 来开启配置类,那么就不要在配置类上使用@Configuration 等能够被 Spring scan 识别到的注解,以免在后续的使用中同一类型的 bean 多个实例

@ConditionalOnProperty

在回到配置不生效问题上来,这里在官方 issue 是有记录的:github.com/spring-proj…
不过这里还是通过分析代码来还原下问题产生的根本原因;这里主要从两个方面来分析:

@ConditionalOnProperty match 逻辑

首先是 @ConditionalOnProperty 在执行计算时,匹配 value 的值来源问题,通过 debug 代码很容易就得到了所有的 source 来源,如下图:

SpringBoot自动配置失效怎么办

从 debug 看,本案例有 4 个来源(具体如上图),实际上从源码来看,source 涵盖了 spring env 所有来源:

[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, StubPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='Config resource 'class path resource [application.properties]' via location 'optional:classpath:/''}]

所以本文案例中不生效原因就是上面这些 PropertySource 都没有 test.enable,也就是 TestProperties 没被刷新,或者其在自动配置类之后才刷新。

@ConditionalOnProperty skip 逻辑

这里主要解释 @ConditionalOnPropert 和 bean 被刷新的逻辑关系,具体实现在 ConditionEvaluator 类中

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {    // 1、没有 Conditional 注解,则扫描时不会跳过当前 bean    // 2、遍历 conditions 进行判断是否满足}

所以对于自动配置类上的注解,Conditional 是作为当前类是否允许被刷新的前提,只有 Conditional 条件满足,才会将当前的自动配置类加入到待刷新 bean 列表中去,如果 Conditional 不满足,这个 bean 将直接被跳过,不会被放到 BeandefinitonMap 中去,也就不会有后续的刷新动作。

@ConditionalOnProperty 作用时机在 BeanDefiniton 被创建之前,其执行时机要比 @EnableConfigurationProperties 作用要早,这也就说明了,为什么 TestProperties 中 test.enable=true, AutoTestConfiguration 也不会刷新的原因了。

看完了这篇文章,相信你对“SpringBoot自动配置失效怎么办”有了一定的了解,如果想了解更多相关知识,欢迎关注编程网行业资讯频道,感谢各位的阅读!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯