Skip to content

QualifierAnnotationAutowireCandidateResolver.checkQualifier does identity checks when comparing arrays used as qualifier fields #32106

@nicholashagen

Description

@nicholashagen

Affects: \spring-beans 6.1.+ (likely newer versions as well)


Overview

We have a custom annotation that we used to attach as qualifier to bean definitions through a post processor. When we added a new String[] array to that annotation, beans no longer matched. This is due to QualifierAnnotationAutowireCandidateResolver.checkQualifier eventually doing if (!expectedValue.equals(actualValue)) { which fails for arrays. The equals methods on arrays purely does identity rather than deep equality of the items.

Deep-Dive

We define a custom annotation as:

@Inherited @Target({ ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Documented @Qualifier @Autowired public @interface MyAnnotation { String value(); String[] inherits() default { }; }

We create custom bean definitions via:

MyAnnotation annotation = getAnnnotation(); // this is handled elsewhere RootBeanDefinition rbd = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(MyFactoryBean.class) getBeanDefinition(); AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(MyAnnotation.class, annotation.value()); qualifier.setAttribute("inherits", annotation.inherits()); rbd.addQualifier(qualifier); registry.registerBeanDefinition(beanName, rbd);

We then try to inject this bean via:

@Component public class MyClass { public MyClass(@MyAnnotation(value = "foo", inherits = "bar") MyClass myClass) { .... } }

This works fine with inherits is not on the annotation, but fails when the array is added.

Eventually this calls QualifierAnnotationAutowireCandidateResolver.checkQualifier. This fetches the qualifier from the bean definition per the post-processor. It then fetches AnnotationUtils.getAnnotationAttributes(annotation); to get all the properties of the target in MyClass (ie: @MyAnnotation(value = "foo", inherits = "bar")). It traverses each property and compares against the qualifier attributes. That eventually results in getting the string array from the qualifier attributes and the annotation property. Those will always end up being different identities since annotations return a new value each time. Even if not, because of how we set the attributes, they would be. It then calls if (!expectedValue.equals(actualValue)) { which fails since arrays use identity comparison not deep equality checks of each item.

Would it make sense here to do a comparison checks that is array-aware and then compare the items rather than purely identity?

We ended up finding a better workaround where we use rbd.setQualifiedElement and copy the annotation into that. This bypasses the qualifier checks and just compares the annotation directly which works as expected.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions