Fixed detection of qualifier annotations on scoped-proxy factory methods
Issue: SPR-11116
This commit is contained in:
parent
dfed8afb26
commit
6bed18004e
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<? extends Annotation> 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<String, Object> 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<String, Object> 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<? extends Annotation> type) {
|
||||
Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
|
||||
return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the given dependency carries a value annotation.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue