I recently upgraded an application from spring-boot 2.1.17 to 2.4.2 and am getting the following error on startup.
java.lang.IllegalStateException: Error processing condition on my.package.MyConfigurationClass.personToUserConverter
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:60)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
at
.....
Caused by: java.lang.IllegalArgumentException: Mismatched number of generics specified
at org.springframework.util.Assert.isTrue(Assert.java:121)
at org.springframework.core.ResolvableType.forClassWithGenerics(ResolvableType.java:1088)
at org.springframework.core.ResolvableType.forClassWithGenerics(ResolvableType.java:1074)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.collectBeanNamesForType(OnBeanCondition.java:240)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:231)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getBeanNamesForType(OnBeanCondition.java:221)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchingBeans(OnBeanCondition.java:169)
at org.springframework.boot.autoconfigure.condition.OnBeanCondition.getMatchOutcome(OnBeanCondition.java:144)
at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
The following (simplified) bean definition demonstrates the issue.
@ConditionalOnMissingBean(value = {Person.class, User.class}, parameterizedContainer = Converter.class)
@Bean
Converter<Person, User> personToUserConverter() {
return new PersonToPepUserConverter();
}
This is fully working when the parameterizedContainer only has one parameter, but fails when there are multiple. Looking through the history, this looks to be caused by https://github.com/spring-projects/spring-boot/issues/17594
Comment From: philwebb
The parameterizedContainer
attribute was primarily added to support classes like ServletRegistrationBean
bean. The idea being that we'd detect beans of a specific type or their wrapper. For example, MyServlet
or ServletRegistrationBean<MyServlet>
would both match a @ConditionalOnBean(value=MyServlet.class, parameterizedContainer=ServletRegistrationBean.class)
.
It looks like you are trying to do something slightly different. I'm guessing you want to register the PersonToPepUserConverter
only if a Converter<Person, User>
bean is missing. There's no out-of-the-box condition annotation to do that I'm afraid. You might be able to write your own Condition
that calls beanFactory.getBeanNamesForType
with a different ResolvableType
.
I'm not sure why things appeared to work in Spring Boot 2.1.17. I suspect the condition wasn't quite doing what you expected.
Comment From: philwebb
I should also mention that bean conditions such as @ConditionalOnMissingBean
only work with auto-configuration class. You shouldn't be used with user @Configuration
. See this javadoc comment:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
Comment From: philwebb
I don't think we can change the existing design. I'm going to close this one with the recommendation that you write a custom Condition
implementation.
Comment From: david-hamilton-bah
Thanks, @philwebb. We ended up forcing the client applications to either use the default bean name or specify the converter's bean name via property if using a custom one. That got us past the issue. I suspect you're right about the parameterizedContainer in 2.1.17, but it did work for both conditions where it was supplied or not. However, it was probably very fragile and could have broken if some other similar converter was defined.