Browse Source

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
pull/1170/merge
Stephane Nicoll 9 years ago
parent
commit
2b0bf9f04a
  1. 15
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
  2. 20
      spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java
  3. 38
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
  4. 4
      spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java

15
spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

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

20
spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

@ -16,6 +16,7 @@
package org.springframework.beans.factory.support; package org.springframework.beans.factory.support;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable; import java.lang.reflect.Executable;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -58,6 +59,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
boolean isFactoryMethodUnique = false; boolean isFactoryMethodUnique = false;
volatile AnnotatedElement qualifiedElement;
/** Package-visible field for caching the determined Class of a given bean definition */ /** Package-visible field for caching the determined Class of a given bean definition */
volatile Class<?> resolvedTargetType; volatile Class<?> resolvedTargetType;
@ -182,6 +185,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
this.allowCaching = original.allowCaching; this.allowCaching = original.allowCaching;
this.targetType = original.targetType; this.targetType = original.targetType;
this.isFactoryMethodUnique = original.isFactoryMethodUnique; this.isFactoryMethodUnique = original.isFactoryMethodUnique;
this.qualifiedElement = original.qualifiedElement;
} }
/** /**
@ -257,6 +261,22 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
this.isFactoryMethodUnique = true; 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. * Check whether the given candidate qualifies as a factory method.
*/ */

38
spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

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

4
spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5));
bd.getPropertyValues().add("name", "myName"); bd.getPropertyValues().add("name", "myName");
bd.getPropertyValues().add("age", "99"); bd.getPropertyValues().add("age", "99");
bd.setQualifiedElement(getClass());
GenericBeanDefinition childBd = new GenericBeanDefinition(); GenericBeanDefinition childBd = new GenericBeanDefinition();
childBd.setParentName("bd"); childBd.setParentName("bd");
@ -138,6 +139,7 @@ public class BeanDefinitionTests {
mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9)); mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9));
assertEquals(new Integer(5), bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue()); assertEquals(new Integer(5), bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue());
assertEquals(getClass(), bd.getQualifiedElement());
} }
} }

Loading…
Cancel
Save