[spring-projects/spring-boot]从 2.7.3 迁移到 3.0.0 后,ConfigurationProperties 无法在 kotlin 组件上运行

2024-06-26 400 views
0

您好,我想看看从 2.7.3 迁移到 3.0.0 后可能出现什么问题 - 我的目标是准备本机应用程序。因此,我了解到可能存在一些问题,@ConditionalOnProperty或者@ConditionalOnBean- 我仍然不明白为什么,但好吧 - 想检查一下。我创建了示例项目,由 spring initializr 生成,您可以在那里找到它(spring boot 版本2.7.3,要查看您需要切换到的问题3.0.0https://github.com/Azbesciak/spring-3-configuration-prop-issue

问题出在以下代码中,它在 2.7.3 中有效

@ConfigurationProperties("my-service")
@ConditionalOnProperty("my-service", havingValue = "c")
@Component
data class SomeBean(var customValue: String = "not set")

但在 3.0.0 中却没有 java.lang.IllegalStateException: Cannot bind @ConfigurationProperties for bean 'someBean'. Ensure that @ConstructorBinding has not been applied to regular bean

我最初尝试使用@Bean服务,如下所示


@ConfigurationProperties("my-service")
data class SomeBean(var customValue: String = "not set")

@Configuration
class MyConfig {
    @Bean
    @ConditionalOnProperty("my-service", havingValue = "c")
    fun someBean() = SomeBean()
}

但它失败了,没有错误,属性也没有设置。

顺便说一句,我以为这应该可行

data class SomeBean(var customValue: String = "not set")

@Configuration
@EnableConfigurationProperties
class MyConfig {
    @Bean
    @ConditionalOnProperty("my-service", havingValue = "c")
    @ConfigurationProperties("my-service")
    fun someBean() = SomeBean()
}

但仅当我将 中的属性名称更改@ConfigurationProperties为超出范围时才会发生这种情况my-servicemy-service.value或者xyz在 中有效2.7.3,在 中3.0.0无效)

我看到了https://github.com/spring-projects/spring-boot/issues/33471,但不确定是否属实。也许我错过了什么?在https://github.com/spring-projects-experimental/spring-native/issues/1679中,您发表了一个声明,基于此,我认为在 3.0.0 中一切都会毫不费力地运行。那么...这是一个错误还是一个功能?

回答

7

Spring Boot 3.0 消除了添加注释的需要@ConstructorBinding,但结果它不再知道您希望如何SomeBean处理。它不知道您是否希望创建常规 bean 或配置属性实例。您可以在此处阅读有关它的信息:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes#improved-constructorbinding-detection

如果你确实希望该类是一个 bean,那么你需要确保有一个@Autowired构造函数:

@ConfigurationProperties("my-service")
@ConditionalOnProperty("my-service", havingValue = "c")
@Component
data class SomeBean(var customValue: String = "not set") {

    @Autowired
    constructor() : this("default")

}

或者您可以删除customValue参数,这样就不会尝试构造函数绑定。

2

作为相关说明,您不应@ConditionalOnBean在常规 bean 上使用。该条件只能应用于自动配置。请参阅 javadoc:

该条件只能匹配迄今为止已由应用程序上下文处理的 bean 定义,因此强烈建议仅在自动配置类上使用此条件。如果另一个自动配置可能创建候选 bean,请确保使用此条件的 bean 在后面运行。

9

从该文档条目...

对于大多数用户

真的吗?Baeldung 上的例子也是这样的。

或者您可以删除customValue参数,这样就不会尝试构造函数绑定。

问题在于这个类是为保存参数而设计的。

有人还说,这种改变是一种进步。但我看不出来。以前的做法有什么问题?为什么现在更好了?为什么我感到迷茫?

假设我不认为它是一个真正的 bean - 我的意思是组件或服务。@Bean那么方法是什么呢?为什么它不起作用呢?我的意思是这个

data class SomeBean(var customValue: String = "not set")

@Configuration
@EnableConfigurationProperties
class MyConfig {
    @Bean
    @ConditionalOnProperty("my-service", havingValue = "c")
    @ConfigurationProperties("xyz")
    fun someBean() = SomeBean()
}

以前能正常工作,为什么现在却没有异常?

7

@philwebb 您能看看上面的评论吗?此外,这种方法是否基本确定,还是仍有可能改变?

9

@Azbesciak 恐怕我不太理解上面的示例,所以也许我们可以退一步思考。您通过定义customValue默认值“未设置”想要实现什么?您是否希望能够xyz.custom-value从您的bean 绑定application.properties到该 bean?

如果是这样,恐怕你的方法行不通。如果你想someBean从中创建MyConfig,那么就不能使用构造函数绑定。你正在控制创建实例someBean。当@ConfigurationProperties在方法上使用时@Bean,绑定发生在创建实例之后。换句话说,你需要setter而不是data class

3

您是否希望能够将 application.properties 中的 xyz.custom-value 绑定到该 bean?

对,就是这样

当 @ConfigurationProperties 用于 @Bean 方法时,绑定发生在实例创建之后

但它在 2.7 中起作用... :(

5

Spring Boot 3.0 在这方面没有任何变化——当应用程序代码创建实例时,永远不可能使用构造函数绑定。如果您想使用构造函数绑定,则需要让 Spring Boot 为您创建实例。

我认为我们并不真正理解您要做什么,以及哪些功能在 2.7 中有效而在 3.0 中无效。为了帮助我们理解,请创建一个适用于 2.7.x 但在升级到 3.0.x 时失败的最小示例。您可以通过将其推送到 GitHub 上的单独存储库或将其压缩并附加到此问题来与我们分享此类示例。

6

抱歉,我应该更清楚地说明我在寻找什么。这是一个完整的@Bean基于 - 的方法示例,我想看看。谢谢您提供一个示例。由于 https://github.com/spring-projects/spring-boot/issues/33710 ,它无法正常工作

5

@wilkinsona 谢谢 - 它会被修复吗,还是之前的一个错误,正如我从 @philwebb 的回复中理解的那样?正如我提到的,其中一些甚至在 Baeldung 上发布过,因此问题范围可能相当广泛……而且它默默地失败也于事无补。

@ConfigurationProperties无论如何,如果没有提供的话,我将需要更换大约 70% 的bean... :/

7

@Azbesciak #33710 中的示例帮助我更好地理解了这个问题,我刚刚推送了一个修复。它也会解决你的问题。如果你想尝试一下,你可以尝试这个3.0.2-SNAPSHOT版本。

1

@philwebb 非常感谢,我也看到最初报告的问题已修复!工作如预期,感谢您的时间和理解。

9

我注意到,当我运行gradle bootBuildImage该项目时,它在运行时失败并出现以下错误

创建名称为“myServiceC”的 bean 时出错:通过构造函数参数 0 表达的不满足的依赖关系:创建名称为“someBean”的 bean 时出错:公共 void com.example.demo.SomeBean.setCustomValue(java.lang.String) 不支持运行时反射

我的问题是:我是否需要为每个这样创建的 bean 添加提示?即使这是@ConfigurationProperties注释的。根据https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.advanced,我以为我不需要这样做,但看起来我需要。

给定的示例对我来说失败了,没有任何提示:

@Configuration(proxyBeanMethods = false)
class ConfigProvider {
    @Bean
    fun appSettings() = AppSettings()
}

@ConfigurationProperties(prefix = "app")
data class AppSettings(
    var appName: String = "",
    var packageName: String = "",
    var serviceAccountFilePath: String = "",
    var serviceScope: String = "",
)
8

问题又出现了吗?我从 3.0.2 升级到了 3.1.1,然后……

无法为 bean“externalJwtTokenParserConfig”绑定 @ConfigurationProperties。确保 @ConstructorBinding 未应用于常规 bean


@Component
@ConditionalOnProperty("auth.jwt.external.enabled", havingValue = "true")
@ConfigurationProperties("auth.jwt.external")
data class ExternalJwtTokenParserConfig(
    var service: ExternalJwtPaths = ExternalJwtPaths(),
)

data class ExternalJwtPaths(
    var uriRoot: String = "",
    var path: String = ""
)