Current InjectionPoint/DependencyDescriptor as factory method argument

Issue: SPR-14033
This commit is contained in:
Juergen Hoeller 2016-03-18 15:12:03 +01:00
parent 2153d88706
commit a95bf6e0fc
3 changed files with 98 additions and 27 deletions

View File

@ -47,6 +47,7 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueH
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.ClassUtils;
import org.springframework.util.MethodInvoker;
@ -69,6 +70,9 @@ import org.springframework.util.StringUtils;
*/
class ConstructorResolver {
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
new NamedThreadLocal<InjectionPoint>("Current injection point");
private final AbstractAutowireCapableBeanFactory beanFactory;
@ -820,11 +824,31 @@ class ConstructorResolver {
protected Object resolveAutowiredArgument(
MethodParameter param, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) {
if (InjectionPoint.class.isAssignableFrom(param.getParameterType())) {
InjectionPoint injectionPoint = currentInjectionPoint.get();
if (injectionPoint == null) {
throw new IllegalStateException("No current InjectionPoint available for " + param);
}
return injectionPoint;
}
return this.beanFactory.resolveDependency(
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
}
static InjectionPoint setCurrentInjectionPoint(InjectionPoint injectionPoint) {
InjectionPoint old = currentInjectionPoint.get();
if (injectionPoint != null) {
currentInjectionPoint.set(injectionPoint);
}
else {
currentInjectionPoint.remove();
}
return old;
}
/**
* Private inner class for holding argument combinations.
*/

View File

@ -54,6 +54,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
@ -1042,37 +1043,43 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return multipleBeans;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(type, descriptor.getResolvableType().toString(), descriptor);
}
return null;
}
if (matchingBeans.size() > 1) {
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (primaryBeanName == null) {
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (descriptor.isRequired()) {
raiseNoSuchBeanDefinitionException(type, descriptor.getResolvableType().toString(), descriptor);
}
return null;
}
if (matchingBeans.size() > 1) {
String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (primaryBeanName == null) {
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(primaryBeanName);
}
return matchingBeans.get(primaryBeanName);
}
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
autowiredBeanNames.add(primaryBeanName);
autowiredBeanNames.add(entry.getKey());
}
return matchingBeans.get(primaryBeanName);
return entry.getValue();
}
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
if (autowiredBeanNames != null) {
autowiredBeanNames.add(entry.getKey());
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
return entry.getValue();
}
private Object resolveMultipleBeans(DependencyDescriptor descriptor, String beanName,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 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.
@ -27,8 +27,13 @@ import org.junit.Test;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Value;
@ -36,6 +41,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
@ -71,7 +77,7 @@ public class ConfigurationClassProcessingTests {
* When complete, the factory is ready to service requests for any {@link Bean} methods
* declared by <var>configClasses</var>.
*/
private ListableBeanFactory initBeanFactory(Class<?>... configClasses) {
private DefaultListableBeanFactory initBeanFactory(Class<?>... configClasses) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
for (Class<?> configClass : configClasses) {
String configBeanName = configClass.getName();
@ -202,6 +208,20 @@ public class ConfigurationClassProcessingTests {
assertNotSame(bar.getSpouse(), baz);
}
@Test
public void configurationWithAdaptivePrototypes() {
DefaultListableBeanFactory factory =
initBeanFactory(ConfigWithPrototypeBean.class, AdaptiveInjectionPoints.class);
AutowiredAnnotationBeanPostProcessor aabpp = new AutowiredAnnotationBeanPostProcessor();
aabpp.setBeanFactory(factory);
factory.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
factory.addBeanPostProcessor(aabpp);
AdaptiveInjectionPoints adaptive = factory.getBean(AdaptiveInjectionPoints.class);
assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName());
assertEquals("adaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName());
}
@Test
public void configurationWithPostProcessor() {
AnnotationConfigApplicationContext factory = new AnnotationConfigApplicationContext();
@ -324,6 +344,26 @@ public class ConfigurationClassProcessingTests {
public TestBean baz() {
return new TestBean("baz");
}
@Bean @Scope("prototype")
public TestBean adaptive1(InjectionPoint ip) {
return new TestBean(ip.getMember().getName());
}
@Bean @Scope("prototype")
public TestBean adaptive2(DependencyDescriptor dd) {
return new TestBean(dd.getMember().getName());
}
}
static class AdaptiveInjectionPoints {
@Autowired @Qualifier("adaptive1")
public TestBean adaptiveInjectionPoint1;
@Autowired @Qualifier("adaptive2")
public TestBean adaptiveInjectionPoint2;
}