Spring Boot 3.4 M2.
It appears that when one defines their own messageSource bean, it's not then possible to inject MessageSourceProperties into the overriding bean for an alternative implementation to reuse certain bits of the configuration schema. That is, if I have my own:
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
// my own implementation...
}
This will fail with:
[org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter] - <Application failed to start due to an exception>
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'org.springframework.boot.autoconfigure.context.MessageSourceProperties' available: expected at
least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I propose that alternative implementations of this bean should still be able to use MessageSourceProperties.
This is of course because the MessageSourceAutoConfiguration defines the following:
@ConditionalOnMissingBean(
name = {"messageSource"},
search = SearchStrategy.CURRENT
)
...and in the same class, MessageSourceProperties is declared as a bean:
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
So when a competing bean is found, this prevents MessageSourceProperties to be created.
I find this somewhat unusual that MessageSourceProperties is created explicitly as a Bean, and the class itself unlike many others is not tagged with @ConfigurationProperties(prefix ="spring.messages"). I suspect that if this were the case, the problem would disappear. The binding would be recognized by the runtime and yet the bean implementation will be skipped in favor of the alternative but there may be a deeper rationale behind the change here.
Thank you for your time!
Comment From: philwebb
I'm not sure why MessageSourceProperties is declared as a @Bean method, that was done a long time ago as part of #9666. I'm pretty sure we can change it. Flagging for team attention in case anyone remembers more about the history of those properties.
Comment From: snicoll
I am not 100% sure but this might be an oversight of https://github.com/spring-projects/spring-boot/issues/13824 where we could have moved this to @EnableConfigurationProperties.
We have a lot of auto-configurations that rely on the absence of a bean, and then only create the configuration. So if we changed this, the user code should still have @EnableConfigurationProperties(MessageSourceProperties.class).
@mmoayyed in the meantime, have you tried to create the bean yourself? Does it work?
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
Comment From: mmoayyed
Unfortunately, no. Defining a bean directly as you note produces the same problem.
Other factors that might be of interest here:
- This app is using JDK dynamic proxies. No CGLib.
- This app is using Spring Cloud and heavily relies on RefreshScope annotations. (None of such beans are annotated with that though)
- The issue appears to only manifest itself when
spring.messagesproperties are defined (which likely makes sense since that would kick the binding ops into effect).
Comment From: wilkinsona
Thanks for the additional info. Unfortunately, it's not clear why that wouldn't work. If you have defined your own MessageSourceProperties bean then such a bean should be available for injection.
If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: mmoayyed
My apologies, I misspoke. My previous test ran a stale copy of the application without that bean defined. To be accurate, yes defining a MessageSourceProperties bean myself DOES indeed remove the issue. Having said that, I am happy to work on a reproducer if you need to see the original problem in action.
Comment From: wilkinsona
Great, thanks. No need for a reproducer as I think we're all on the same page now. I think we should be able to change things such that MessageSourceProperties is annotated with @ConfigurationProperties("spring.messages") and enabled through @EnableConfigurationProperties(MessageSourceProperties.class) on MessageSourceAutoConfiguration.
Note that, as @snicoll mentions above, once this change is in place, if you've defined your own messageSource bean and want to inject MessageSourceProperties, you will have to use @EnableConfigruationProperties(MessageSourceProperties.class). This will be necessary because the presence of the messageSource bean will cause MessageSourceAutoConfiguration to back off so it won't be enabling the properties any more.
Comment From: mmoayyed
Understood, thank you very much everyone for the fix and the notes.
Comment From: quaff
Does MessageSourceProperties backoff if custom messageSource bean present due to the @ConditionalOnMissingBean on class level ?
Comment From: wilkinsona
Yes.
Comment From: quaff
Yes.
It's not consistent with other AutoConfiguration's which will not backoff ConfigurationProperties, @EnableConfigurationProperties(MessageSourceProperties.class) is required by application if they want build its own MessageSource.
Comment From: bclozel
@quaff Stéphane already shared what developers should to to get properties even if the auto-configuration backs off. Can you explain your use case and how you're defining your custom MessageSource?
Comment From: wilkinsona
Also, why don't you think it's consistent? Any class annotated with @EnableConfigurationProperties and one or more conditions will not enable the configuration properties if one of the conditions does not match.
Comment From: quaff
My point is @ConditionalOnMissingBean should be annotated on method messageSource() not MessageSourceAutoConfiguration like other AutoConfigurations, then application could customize like this:
@Configuration
@EnableConfigurationProperties(MessageSourceProperties.class) // it should not be required
public class MyMessageSourceConfiguration {
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
...
}
}
Comment From: bclozel
If we do what you're suggesting in our codebase, then Spring Boot apps would get dozens of *Properties beans even if they're not used at all as the application already provided its opinion. This is consistently applied in our codebase as soon as the presence of a bean implies that developers are taking full control over the configuration. This is the case for the MVC auto-configuration, for example.
Comment From: quaff
I'm not against that, just point it out that developers may be confused, since most of *Properties doesn't backoff.