-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
Problem statement
Between spring-beans 6.2.12 and 6.2.14 something changed when it comes to resolving Collection-type beans. I doubt it was an intentional change as it may break many projects so I thought it's reasonable to report the issue and get the clarification from your team. Could you clarify if the change in behaviour is expected?
See more details on the issue and pointing out the possible culprit below.
Elaboration on the issue
When the following 2 libraries are on the classpath, List<BulkheadConfigCustomizer> argument resolves to List<Worker> which is wrong and results in a ClassCastException (see stacktrace below).
io.github.resilience4j:resilience4j-spring-boot3:2.2.0
public abstract class AbstractBulkheadConfigurationOnMissingBean {
...
@Bean
@ConditionalOnMissingBean(name = "compositeBulkheadCustomizer")
@Qualifier("compositeBulkheadCustomizer")
public CompositeCustomizer<BulkheadConfigCustomizer> compositeBulkheadCustomizer(
@Autowired(required = false) List<BulkheadConfigCustomizer> customizers) {
return new CompositeCustomizer<>(customizers);
}io.temporal:temporal-spring-boot-autoconfigure:1.30.1
public class RootNamespaceAutoConfiguration {
...
@Primary
@Bean(name = "temporalWorkers")
@Conditional(WorkersPresentCondition.class)
public Collection<Worker> workers(
@Qualifier("temporalWorkersTemplate") WorkersTemplate workersTemplate) {
Collection<Worker> workers = workersTemplate.getWorkers();
workers.forEach(
worker -> beanFactory.registerSingleton("temporalWorker-" + worker.getTaskQueue(), worker));
return workers;
}Stacktrace:
Caused by: java.lang.ClassCastException: class io.temporal.worker.Worker cannot be cast to class io.github.resilience4j.common.CustomizerWithName (io.temporal.worker.Worker and io.github.resilience4j.common.CustomizerWithName are in unnamed module of loader 'app')
at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
at io.github.resilience4j.common.CompositeCustomizer.(CompositeCustomizer.java:34)
at io.github.resilience4j.springboot3.bulkhead.autoconfigure.AbstractBulkheadConfigurationOnMissingBean.compositeBulkheadCustomizer(AbstractBulkheadConfigurationOnMissingBean.java:67)
at io.github.resilience4j.springboot3.bulkhead.autoconfigure.BulkheadConfigurationOnMissingBean$$SpringCGLIB$$0.CGLIB$compositeBulkheadCustomizer$8()
at io.github.resilience4j.springboot3.bulkhead.autoconfigure.BulkheadConfigurationOnMissingBean$$SpringCGLIB$$FastClass$$1.invoke()
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:400)
at io.github.resilience4j.springboot3.bulkhead.autoconfigure.BulkheadConfigurationOnMissingBean$$SpringCGLIB$$0.compositeBulkheadCustomizer()
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:172)
... 49 more
Tracing down the culprit
spring-beans:6.2.12
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
...
@Override
public boolean hasQualifier(DependencyDescriptor descriptor) {
for (Annotation annotation : descriptor.getAnnotations()) {
if (isQualifier(annotation.annotationType())) {
return true;
}
}
return false; // <--- RETURNS FALSE, NO BEAN IS RESOLVED
}spring-beans:6.2.14
@Override
public boolean hasQualifier(DependencyDescriptor descriptor) {
for (Annotation annotation : descriptor.getAnnotations()) {
if (isQualifier(annotation.annotationType())) {
return true;
}
}
MethodParameter methodParam = descriptor.getMethodParameter(); // compositeBulkheadCustomizer
if (methodParam != null) {
for (Annotation annotation : methodParam.getMethodAnnotations()) { // @Qualifier("compositeBulkheadCustomizer")
if (isQualifier(annotation.annotationType())) {
return true; // <--- RETURNS TRUE, CAUSES A List PRESENT IN THE CONTEXT TO BE RESOLVED TO List<SomeSpecificType>
}
}
}
return false;
}I don't see why @Qualifier annotation present on compositeBulkheadCustomizer method would be supposed to mean "yes we accept any List present in the Spring context as List<BulkheadConfigCustomizer> argument".