【问题标题】:Custom springboot autoconfiguration not detecting beans自定义 Spring Boot 自动配置未检测到 bean
【发布时间】:2016-03-13 15:07:39
【问题描述】:

我开发了一个自定义 Spring Boot 自动配置来简化使用专有消息传递库。

主要的自动配置类基本如下:

@Configuration
@ConditionalOnClass({LibServer.class, LibClient.class})
@EnableConfigurationProperties(LibProperties.class)
public class LibAutoConfiguration {

    @Autowired
    LibProperties props;

    @Bean
    @ConditionalOnMissingBean(LibServer.class)
    public LibServer lbServ() {
        // create and configure a server object
    }

    @Bean
    @ConditionalOnMissingBean(LibClient.class)
    public LibClient lbClient() {
        //create and configure a client object
    }
}

然而,条件注释似乎没有检测到主 @SpringBootApplication 注释类中声明的 bean。

它只检测在单独的 @Configuration 注释类中声明的 bean。

也就是说,如果我在主类中放置两个 @Bean 注释方法返回一个 LibServer 和一个 LibClient 对象,我最终会得到两个 LibServer 和两个 LibClient 对象(自动配置的对象和明确声明的)在上下文中。

本机 Spring Boot 自动配置(例如 DataSource one)也可以检测在主类中声明的 bean(例如 @Bean 注释的 jdbcTemplate 方法)。

即使是在主类中声明的 bean,我如何才能正确检测 bean?

编辑

展示该行为的完整多模块 maven 项目位于 https://github.com/AlexFalappa/spring-boot-testcase

【问题讨论】:

  • 你的主类是什么样的,你如何运行你的应用程序?
  • 您不会自己导入LibAutoConfiguration,是吗?自动配置类必须spring.factories 中定义,而不是直接加载。这在这种情况下很重要,因为我们需要先处理所有用户配置,然后检查是否必须创建这些 bean。
  • @StéphaneNic​​oll:我的自定义自动配置在一个 maven 模块中,其中包含相关的spring.factories 文件,并且还通过声明对专有消息传递库的依赖关系作为启动模块。该应用程序位于依赖于前者的不同 Maven 模块中。
  • @Morfic:主类是一个普通的 Spring Boot 应用程序,注释为@SpringBootApplication

标签: java spring spring-boot


【解决方案1】:

如果您在 application.properties (logging.level.org.springframework=DEBUG) 中设置调试日志级别,您会注意到 Spring 将检测这两个定义。但是,您还会看到 the order in which this happens may not be what you expected,因为它首先从库配置实例化 bean,然后从您的 main 类 实例化 bean,因此您得到 2 个实例(去除时间戳以使其更友好):

Bean 定义

o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.spring.boot.libbo.LibAutoConfiguration.lbServ()
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.autoconfigure.condition.BeanTypeRegistry'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'autoConfigurationReport'
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.spring.boot.libbo.LibAutoConfiguration.lbClient()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.DemoLibboApplication.libServ()
a.ConfigurationClassBeanDefinitionReader : Registering bean definition for @Bean method af.DemoLibboApplication.libClient()

Bean 实例化

o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibServer
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'lbServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'lbServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'libAutoConfiguration'
Autoconfiguring LibClient
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'lbClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'lbClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'lib.CONFIGURATION_PROPERTIES'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'libServ' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'libServ'
o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory     : Creating instance of bean 'libClient'
o.s.b.f.s.DefaultListableBeanFactory     : Returning cached instance of singleton bean 'demoLibboApplication'
o.s.b.f.s.DefaultListableBeanFactory     : Eagerly caching bean 'libClient' to allow for resolving potential circular references
o.s.b.f.s.DefaultListableBeanFactory     : Finished creating instance of bean 'libClient'

您还可以在 AUTO-CONFIGURATION REPORT 中看到,在您当前的实现中,当评估 LibAutoConfiguration 中的条件时,它们匹配并且通常它会创建 bean:

Positive matches:
-----------------
...
LibAutoConfiguration#lbClient matched
  - @ConditionalOnMissingBean (types: af.libbo.LibClient; SearchStrategy: all) found no beans (OnBeanCondition)

LibAutoConfiguration#lbServ matched
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found no beans (OnBeanCondition)
...

但是,如果您将相同的条件添加到您的主类,您会看到它将根据LibAutoConfiguration 中的定义创建bean,并且在尝试为@ 创建这些bean 时987654330@,它实际上会找到之前创建的bean并跳过实例化:

Negative matches:
-----------------
...
DemoLibboApplication#libClient did not match
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)

DemoLibboApplication#libServ did not match
  - @ConditionalOnMissingBean (types: af.libbo.LibServer; SearchStrategy: all) found the following [lbServ] (OnBeanCondition)
...

【讨论】:

    【解决方案2】:

    您不会自己导入 LibAutoConfiguration 吧?

    这是提示:您的主类位于您的自动配置类的父包中。所以你实际上是通过组件扫描自己导入@Configuration。事实证明,当您处理该类时(通过显式导入而不是通过自动配置),尚未创建任何 bean,因此它确实创建了它们。稍后处理您的应用程序类并创建这些 bean。

    如果您将定义移至其他位置,它可能会起作用(正如您自己使用LibConfig 所了解的那样),但这不是确定性的。

    TL;DR 确保您的自动配置代码位于单独的空间中并且不是组件扫描的目标。我已将您的 DemoLibboApplication 移至 demo 包,它按预期工作。

    【讨论】:

    • 就是这样。也许这值得在 Spring Boot 文档中的 Creating your own auto-configuration 部分中指出?
    • 好的,我已经创建了#5404
    • 如果这些类不打算被扫描,那么用 @Configuration 注释自动配置类有什么意义?如果这纯粹是信息性的,那么将它们设为@AutoConfiguration 在类路径上未注册并在 spring.factories 中未注册时抛出异常肯定会减少混淆。
    • @Configuration 是使用 @Bean 工厂方法注册 bean 定义的组件的原型。这与类路径扫描无关,尽管如果您将组件扫描指向它,这是一个副作用。上下文将为该类创建一个代理,因此如果您在内部调用其他工厂 bean 方法以进行显式依赖注入,则可以保证检索到与定义的范围匹配的 bean。我们最近引入了一个 proxyBeanMethods 属性来关闭它。
    • 您的@AutoConfiguration 建议有相反的问题(忽略上述方法不起作用的事实)。如果您忘记在 spring.factories 中注册它,那么它不是自动配置。这比 IMO 的现状要糟糕得多。
    猜你喜欢
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 2018-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多