diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index 39b160e15f..a0d64955f5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -133,8 +133,7 @@ public interface BeanFactory { * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @return an instance of the bean - * @throws NoSuchBeanDefinitionException if there is no bean definition - * with the specified name + * @throws NoSuchBeanDefinitionException if there is no bean with the specified name * @throws BeansException if the bean could not be obtained */ Object getBean(String name) throws BeansException; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java index 4a6eda2bbf..cd0812d187 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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. @@ -29,6 +29,8 @@ import org.springframework.util.ObjectUtils; /** * A simple descriptor for an injection point, pointing to a method/constructor * parameter or a field. Exposed by {@link UnsatisfiedDependencyException}. + * Also available as an argument for factory methods, reacting to the + * requesting injection point for building a customized bean instance. * * @author Juergen Hoeller * @since 4.3 diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java index 49b3abf9a7..6d17d6f161 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -337,8 +337,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * including its bean name. *

This is effectively a variant of {@link #getBean(Class)} which preserves the * bean name of the matching instance. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. + * @param requiredType type the bean must match; can be an interface or superclass * @return the bean name plus bean instance * @throws NoSuchBeanDefinitionException if no matching bean was found * @throws NoUniqueBeanDefinitionException if more than one matching bean was found @@ -348,6 +347,22 @@ public interface AutowireCapableBeanFactory extends BeanFactory { */ NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException; + /** + * Resolve a bean instance for the given bean name, providing a dependency descriptor + * for exposure to target factory methods. + *

This is effectively a variant of {@link #getBean(String, Class)} which supports + * factory methods with an {@link org.springframework.beans.factory.InjectionPoint} + * argument. + * @param name the name of the bean to look up + * @param descriptor the dependency descriptor for the requesting injection point + * @return the corresponding bean instance + * @throws NoSuchBeanDefinitionException if there is no bean with the specified name + * @throws BeansException if the bean could not be created + * @since 5.1.5 + * @see #getBean(String, Class) + */ + Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException; + /** * Resolve the specified dependency against the beans defined in this factory. * @param descriptor the descriptor for the dependency (field/method/constructor) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 13bb3cf44e..6ccc3bbf8c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -58,6 +58,7 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; @@ -337,12 +338,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac return initializeBean(beanName, existingBean, bd); } - @Override - @Nullable - public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException { - return resolveDependency(descriptor, requestingBeanName, null, null); - } - //------------------------------------------------------------------------- // Specialized methods for fine-grained control over the bean lifecycle @@ -446,6 +441,28 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } + //------------------------------------------------------------------------- + // Delegate methods for resolving injection points + //------------------------------------------------------------------------- + + @Override + public Object resolveBeanByName(String name, DependencyDescriptor descriptor) { + InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); + try { + return getBean(name, descriptor.getDependencyType()); + } + finally { + ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); + } + } + + @Override + @Nullable + public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException { + return resolveDependency(descriptor, requestingBeanName, null, null); + } + + //--------------------------------------------------------------------- // Implementation of relevant AbstractBeanFactory template methods //--------------------------------------------------------------------- diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java index 772d5972a1..2ed96949b3 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -512,13 +512,19 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean Set autowiredBeanNames; String name = element.name; - if (this.fallbackToDefaultTypeMatch && element.isDefaultName && - factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) { - autowiredBeanNames = new LinkedHashSet<>(); - resource = ((AutowireCapableBeanFactory) factory).resolveDependency( - element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null); - if (resource == null) { - throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); + if (factory instanceof AutowireCapableBeanFactory) { + AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory; + DependencyDescriptor descriptor = element.getDependencyDescriptor(); + if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { + autowiredBeanNames = new LinkedHashSet<>(); + resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null); + if (resource == null) { + throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); + } + } + else { + resource = beanFactory.resolveBeanByName(name, descriptor); + autowiredBeanNames = Collections.singleton(name); } } else { 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 5d172cbb01..79a165b2db 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-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.Supplier; +import javax.annotation.Resource; import javax.inject.Provider; import org.junit.Rule; @@ -228,6 +229,22 @@ public class ConfigurationClassProcessingTests { ctx.close(); } + @Test + public void configurationWithAdaptiveResourcePrototypes() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ConfigWithPrototypeBean.class, AdaptiveResourceInjectionPoints.class); + ctx.refresh(); + + AdaptiveResourceInjectionPoints adaptive = ctx.getBean(AdaptiveResourceInjectionPoints.class); + assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName()); + assertEquals("setAdaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); + + adaptive = ctx.getBean(AdaptiveResourceInjectionPoints.class); + assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName()); + assertEquals("setAdaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); + ctx.close(); + } + @Test public void configurationWithPostProcessor() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); @@ -444,6 +461,21 @@ public class ConfigurationClassProcessingTests { } + @Scope("prototype") + static class AdaptiveResourceInjectionPoints { + + @Resource(name = "adaptive1") + public TestBean adaptiveInjectionPoint1; + + public TestBean adaptiveInjectionPoint2; + + @Resource(name = "adaptive2") + public void setAdaptiveInjectionPoint2(TestBean adaptiveInjectionPoint2) { + this.adaptiveInjectionPoint2 = adaptiveInjectionPoint2; + } + } + + static class ConfigWithPostProcessor extends ConfigWithPrototypeBean { @Value("${myProp}") diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java index 99a575f17e..e41e12d4f1 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -411,6 +411,11 @@ class StubWebApplicationContext implements WebApplicationContext { throw new UnsupportedOperationException("Dependency resolution not supported"); } + @Override + public Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException { + throw new UnsupportedOperationException("Dependency resolution not supported"); + } + @Override @Nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) {