From a95bf6e0fc5206c662e81811d3bb964c9003aad0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Mar 2016 15:12:03 +0100 Subject: [PATCH] Current InjectionPoint/DependencyDescriptor as factory method argument Issue: SPR-14033 --- .../factory/support/ConstructorResolver.java | 24 ++++++++ .../support/DefaultListableBeanFactory.java | 57 +++++++++++-------- .../ConfigurationClassProcessingTests.java | 44 +++++++++++++- 3 files changed, 98 insertions(+), 27 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 66fd13c633..ff7d4d8b07 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -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 currentInjectionPoint = + new NamedThreadLocal("Current injection point"); + private final AbstractAutowireCapableBeanFactory beanFactory; @@ -820,11 +824,31 @@ class ConstructorResolver { protected Object resolveAutowiredArgument( MethodParameter param, String beanName, Set 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. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 72b8c99b5f..7d6d6b2e9c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -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 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 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 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 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, diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java index 78b990b173..04fa5290e2 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java @@ -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 configClasses. */ - 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; }