diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java index ce4f4e926b0..2f2a89887c0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java +++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java @@ -50,17 +50,17 @@ public abstract class ScopedProxyUtils { String originalBeanName = definition.getBeanName(); BeanDefinition targetDefinition = definition.getBeanDefinition(); + String targetBeanName = getTargetBeanName(originalBeanName); // Create a scoped proxy definition for the original bean name, // "hiding" the target bean in an internal target definition. RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); + proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName)); proxyDefinition.setOriginatingBeanDefinition(targetDefinition); proxyDefinition.setSource(definition.getSource()); proxyDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - String targetBeanName = getTargetBeanName(originalBeanName); proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName); - if (proxyTargetClass) { targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 76c36efbba9..6eb7eae43d6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -26,7 +26,9 @@ import org.springframework.beans.SimpleTypeConverter; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.AutowireCandidateResolver; @@ -227,18 +229,22 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan Class type = annotation.annotationType(); RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition(); + AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName()); if (qualifier == null) { qualifier = bd.getQualifier(ClassUtils.getShortName(type)); } if (qualifier == null) { - Annotation targetAnnotation = null; - Method resolvedFactoryMethod = bd.getResolvedFactoryMethod(); - if (resolvedFactoryMethod != null) { - targetAnnotation = AnnotationUtils.getAnnotation(resolvedFactoryMethod, type); + // First, check annotation on factory method, if applicable + Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type); + if (targetAnnotation == null) { + RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd); + if (dbd != null) { + targetAnnotation = getFactoryMethodAnnotation(dbd, type); + } } if (targetAnnotation == null) { - // look for matching annotation on the target class + // Look for matching annotation on the target class if (this.beanFactory != null) { Class beanType = this.beanFactory.getType(bdHolder.getBeanName()); if (beanType != null) { @@ -253,30 +259,31 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan return true; } } + Map attributes = AnnotationUtils.getAnnotationAttributes(annotation); if (attributes.isEmpty() && qualifier == null) { - // if no attributes, the qualifier must be present + // If no attributes, the qualifier must be present return false; } for (Map.Entry entry : attributes.entrySet()) { String attributeName = entry.getKey(); Object expectedValue = entry.getValue(); Object actualValue = null; - // check qualifier first + // Check qualifier first if (qualifier != null) { actualValue = qualifier.getAttribute(attributeName); } if (actualValue == null) { - // fall back on bean definition attribute + // Fall back on bean definition attribute actualValue = bd.getAttribute(attributeName); } if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) && expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) { - // fall back on bean name (or alias) match + // Fall back on bean name (or alias) match continue; } if (actualValue == null && qualifier != null) { - // fall back on default, but only if the qualifier is present + // Fall back on default, but only if the qualifier is present actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName); } if (actualValue != null) { @@ -289,6 +296,25 @@ public class QualifierAnnotationAutowireCandidateResolver implements AutowireCan return true; } + protected RootBeanDefinition getResolvedDecoratedDefinition(RootBeanDefinition rbd) { + BeanDefinitionHolder decDef = rbd.getDecoratedDefinition(); + if (decDef != null && this.beanFactory instanceof ConfigurableListableBeanFactory) { + ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory; + if (clbf.containsBeanDefinition(decDef.getBeanName())) { + BeanDefinition dbd = clbf.getMergedBeanDefinition(decDef.getBeanName()); + if (dbd instanceof RootBeanDefinition) { + return (RootBeanDefinition) dbd; + } + } + } + return null; + } + + protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class type) { + Method resolvedFactoryMethod = bd.getResolvedFactoryMethod(); + return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null); + } + /** * Determine whether the given dependency carries a value annotation. diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java index ba336cdb8f2..bd2808eb0df 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/BeanMethodQualificationTests.java @@ -20,7 +20,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import org.junit.Test; -import org.springframework.tests.sample.beans.TestBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -28,7 +27,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; +import org.springframework.tests.sample.beans.TestBean; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -51,6 +53,24 @@ public class BeanMethodQualificationTests { assertThat(pojo.testBean.getName(), equalTo("interesting")); } + @Test + public void testScoped() { + AnnotationConfigApplicationContext ctx = + new AnnotationConfigApplicationContext(ScopedConfig.class, StandardPojo.class); + assertFalse(ctx.getBeanFactory().containsSingleton("testBean1")); + StandardPojo pojo = ctx.getBean(StandardPojo.class); + assertThat(pojo.testBean.getName(), equalTo("interesting")); + } + + @Test + public void testScopedProxy() { + AnnotationConfigApplicationContext ctx = + new AnnotationConfigApplicationContext(ScopedProxyConfig.class, StandardPojo.class); + assertTrue(ctx.getBeanFactory().containsSingleton("testBean1")); // a shared scoped proxy + StandardPojo pojo = ctx.getBean(StandardPojo.class); + assertThat(pojo.testBean.getName(), equalTo("interesting")); + } + @Test public void testCustom() { AnnotationConfigApplicationContext ctx = @@ -63,7 +83,8 @@ public class BeanMethodQualificationTests { @Configuration static class StandardConfig { - @Bean @Lazy @Qualifier("interesting") + + @Bean @Qualifier("interesting") @Lazy public TestBean testBean1() { return new TestBean("interesting"); } @@ -74,13 +95,43 @@ public class BeanMethodQualificationTests { } } + @Configuration + static class ScopedConfig { + + @Bean @Qualifier("interesting") @Scope("prototype") + public TestBean testBean1() { + return new TestBean("interesting"); + } + + @Bean @Qualifier("boring") @Scope("prototype") + public TestBean testBean2() { + return new TestBean("boring"); + } + } + + @Configuration + static class ScopedProxyConfig { + + @Bean @Qualifier("interesting") @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) + public TestBean testBean1() { + return new TestBean("interesting"); + } + + @Bean @Qualifier("boring") @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) + public TestBean testBean2() { + return new TestBean("boring"); + } + } + @Component @Lazy static class StandardPojo { + @Autowired @Qualifier("interesting") TestBean testBean; } @Configuration static class CustomConfig { + @InterestingBean public TestBean testBean1() { return new TestBean("interesting"); @@ -94,6 +145,7 @@ public class BeanMethodQualificationTests { @InterestingPojo static class CustomPojo { + @InterestingNeed TestBean testBean; }