Add Qualified element on RootBeanDefinition

Improve RootBeanDefinition to specify an AnnotatedElement that holds
qualifier information. When such element is present, any qualifier that
it defines will be used to find a matching candidate.

Issue: SPR-14725
This commit is contained in:
Stephane Nicoll 2016-09-15 16:02:10 +02:00
parent f24ce76edb
commit 2b0bf9f04a
4 changed files with 68 additions and 9 deletions

View File

@ -17,6 +17,7 @@
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
@ -49,6 +50,7 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 2.5
* @see AutowireCandidateQualifier
* @see Qualifier
@ -225,8 +227,12 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null) {
// First, check annotation on factory method, if applicable
Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type);
// First, check annotation on qualified element, if any
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
// Then, check annotation on factory method, if applicable
if (targetAnnotation == null) {
targetAnnotation = getFactoryMethodAnnotation(bd, type);
}
if (targetAnnotation == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
if (dbd != null) {
@ -291,6 +297,11 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
return true;
}
protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
AnnotatedElement qualifiedElement = bd.getQualifiedElement();
return (qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null);
}
protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? extends Annotation> type) {
Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);

View File

@ -16,6 +16,7 @@
package org.springframework.beans.factory.support;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
@ -58,6 +59,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
boolean isFactoryMethodUnique = false;
volatile AnnotatedElement qualifiedElement;
/** Package-visible field for caching the determined Class of a given bean definition */
volatile Class<?> resolvedTargetType;
@ -182,6 +185,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
this.allowCaching = original.allowCaching;
this.targetType = original.targetType;
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
this.qualifiedElement = original.qualifiedElement;
}
/**
@ -257,6 +261,22 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
this.isFactoryMethodUnique = true;
}
/**
* Specify the {@link AnnotatedElement} defining qualifiers.
* @since 4.3.3
*/
public void setQualifiedElement(AnnotatedElement qualifiedElement) {
this.qualifiedElement = qualifiedElement;
}
/**
* Return the {@link AnnotatedElement} defining qualifiers, if any.
* @since 4.3.3
*/
public AnnotatedElement getQualifiedElement() {
return this.qualifiedElement;
}
/**
* Check whether the given candidate qualifies as a factory method.
*/

View File

@ -63,6 +63,7 @@ import org.springframework.tests.sample.beans.ITestBean;
import org.springframework.tests.sample.beans.IndexedTestBean;
import org.springframework.tests.sample.beans.NestedTestBean;
import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.SerializationTestUtils;
import static org.junit.Assert.*;
@ -1026,14 +1027,35 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
bf.registerBeanDefinition("testBean", bd);
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
bf.registerBeanDefinition("dependencyBean", bd);
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("testBean"), bean.getTestBean());
assertSame(bf.getBean("dependencyBean"), bean.getTestBean());
bf.destroySingletons();
}
@Test
public void testObjectFactoryQualifierProviderInjection() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
bd.setQualifiedElement(ReflectionUtils.findMethod(getClass(), "testBeanQualifierProvider"));
bf.registerBeanDefinition("dependencyBean", bd);
bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class));
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
assertSame(bf.getBean("dependencyBean"), bean.getTestBean());
bf.destroySingletons();
}
@Qualifier("testBean")
private void testBeanQualifierProvider() {}
@Test
public void testObjectFactorySerialization() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@ -1588,11 +1610,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
rbd.setFactoryBeanName("mocksControl");
rbd.setFactoryMethodName("createMock");
rbd.getConstructorArgumentValues().addGenericArgumentValue(Repository.class);
bf.registerBeanDefinition("integerRepo", rbd);
rbd.setQualifiedElement(ReflectionUtils.findField(getClass(), "integerRepositoryQualifierProvider"));
bf.registerBeanDefinition("integerRepository", rbd); // Bean name not matching qualifier
RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean");
Repository<?> sr = bf.getBean("stringRepo", Repository.class);
Repository<?> ir = bf.getBean("integerRepo", Repository.class);
Repository<?> ir = bf.getBean("integerRepository", Repository.class);
assertSame(sr, bean.stringRepository);
assertSame(ir, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length);
@ -1606,9 +1629,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(1, bean.stringRepositoryMap.size());
assertSame(1, bean.integerRepositoryMap.size());
assertSame(sr, bean.stringRepositoryMap.get("stringRepo"));
assertSame(ir, bean.integerRepositoryMap.get("integerRepo"));
assertSame(ir, bean.integerRepositoryMap.get("integerRepository"));
}
@Qualifier("integerRepo")
private Repository<?> integerRepositoryQualifierProvider;
@Test
public void testGenericsBasedFieldInjectionWithSimpleMatch() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -126,6 +126,7 @@ public class BeanDefinitionTests {
bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5));
bd.getPropertyValues().add("name", "myName");
bd.getPropertyValues().add("age", "99");
bd.setQualifiedElement(getClass());
GenericBeanDefinition childBd = new GenericBeanDefinition();
childBd.setParentName("bd");
@ -138,6 +139,7 @@ public class BeanDefinitionTests {
mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9));
assertEquals(new Integer(5), bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue());
assertEquals(getClass(), bd.getQualifiedElement());
}
}