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 58b2fd97bb6..49d235a6595 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-2015 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. @@ -153,12 +153,12 @@ public interface BeanFactory { /** * Return the bean instance that uniquely matches the given object type, if any. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. *

This method goes into {@link ListableBeanFactory} by-type lookup territory * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. + * @param requiredType type the bean must match; can be an interface or superclass. + * {@code null} is disallowed. * @return an instance of the single bean matching the required type * @throws NoSuchBeanDefinitionException if no bean of the given type was found * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java index e1e5ed33f57..9f2453ff653 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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. @@ -17,10 +17,10 @@ package org.springframework.beans.factory; /** - * Counterpart of BeanNameAware. Returns the bean name of an object. + * Counterpart of {@link BeanNameAware}. Returns the bean name of an object. * - *

This interface can be introduced to avoid a brittle dependence - * on bean name in objects used with Spring IoC and Spring AOP. + *

This interface can be introduced to avoid a brittle dependence on + * bean name in objects used with Spring IoC and Spring AOP. * * @author Rod Johnson * @since 2.0 @@ -29,7 +29,7 @@ package org.springframework.beans.factory; public interface NamedBean { /** - * Return the name of this bean in a Spring bean factory. + * Return the name of this bean in a Spring bean factory, if known. */ String getBeanName(); 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 43602b48ebf..bde3d36e59b 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 @@ -21,6 +21,8 @@ import java.util.Set; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; /** * Extension of the {@link org.springframework.beans.factory.BeanFactory} @@ -154,15 +156,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory { */ Object configureBean(Object existingBean, String beanName) throws BeansException; - /** - * Resolve the specified dependency against the beans defined in this factory. - * @param descriptor the descriptor for the dependency - * @param beanName the name of the bean which declares the present dependency - * @return the resolved object, or {@code null} if none found - * @throws BeansException if dependency resolution failed - */ - Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException; - //------------------------------------------------------------------------- // Specialized methods for fine-grained control over the bean lifecycle @@ -312,18 +305,55 @@ public interface AutowireCapableBeanFactory extends BeanFactory { */ void destroyBean(Object existingBean); + + //------------------------------------------------------------------------- + // Delegate methods for resolving injection points + //------------------------------------------------------------------------- + + /** + * Resolve the bean instance that uniquely matches the given object type, if any, + * 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. + * @return the bean name plus bean instance + * @throws NoSuchBeanDefinitionException if no bean of the given type was found + * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found + * @throws BeansException if the bean could not be created + * @since 4.3.3 + * @see #getBean(Class) + */ + NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException; + + /** + * Resolve the specified dependency against the beans defined in this factory. + * @param descriptor the descriptor for the dependency + * @param requestingBeanName the name of the bean which declares the present dependency + * @return the resolved object, or {@code null} if none found + * @throws NoSuchBeanDefinitionException if no matching bean was found + * @throws NoUniqueBeanDefinitionException if more than one matching bean was found + * @throws BeansException if dependency resolution failed for any other reason + * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter) + * @since 2.5 + */ + Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException; + /** * Resolve the specified dependency against the beans defined in this factory. * @param descriptor the descriptor for the dependency - * @param beanName the name of the bean which declares the present dependency - * @param autowiredBeanNames a Set that all names of autowired beans (used for - * resolving the present dependency) are supposed to be added to - * @param typeConverter the TypeConverter to use for populating arrays and - * collections + * @param requestingBeanName the name of the bean which declares the present dependency + * @param autowiredBeanNames a Set that all names of autowired beans (used for resolving + * the present dependency) are supposed to be added to + * @param typeConverter the TypeConverter to use for populating arrays and collections * @return the resolved object, or {@code null} if none found - * @throws BeansException if dependency resolution failed + * @throws NoSuchBeanDefinitionException if no matching bean was found + * @throws NoUniqueBeanDefinitionException if more than one matching bean was found + * @throws BeansException if dependency resolution failed for any other reason + * @see DependencyDescriptor + * @since 2.5 */ - Object resolveDependency(DependencyDescriptor descriptor, String beanName, + Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java new file mode 100644 index 00000000000..b950aef98db --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java @@ -0,0 +1,56 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory.config; + +import org.springframework.beans.factory.NamedBean; + +/** + * A simple holder for a given bean name plus bean instance. + * + * @author Juergen Hoeller + * @since 4.3.3 + * @see AutowireCapableBeanFactory#resolveNamedBean(Class) + */ +public class NamedBeanHolder implements NamedBean { + + private final String beanName; + + private final T beanInstance; + + + /** + * Create a new holder for the given bean name plus instance. + */ + public NamedBeanHolder(String beanName, T beanInstance) { + this.beanName = beanName; + this.beanInstance = beanInstance; + } + + + @Override + public String getBeanName() { + return this.beanName; + } + + /** + * Return the corresponding bean instance. + */ + public T getBeanInstance() { + return this.beanInstance; + } + +} 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 c30e6740dfb..2e47ff2be7c 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 @@ -32,7 +32,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -62,11 +61,13 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.config.NamedBeanHolder; import org.springframework.core.OrderComparator; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; @@ -326,43 +327,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public T getBean(Class requiredType, Object... args) throws BeansException { - Assert.notNull(requiredType, "Required type must not be null"); - String[] beanNames = getBeanNamesForType(requiredType); - if (beanNames.length > 1) { - ArrayList autowireCandidates = new ArrayList<>(); - for (String beanName : beanNames) { - if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { - autowireCandidates.add(beanName); - } - } - if (autowireCandidates.size() > 0) { - beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); - } - } - if (beanNames.length == 1) { - return getBean(beanNames[0], requiredType, args); - } - else if (beanNames.length > 1) { - Map candidates = new HashMap<>(); - for (String beanName : beanNames) { - candidates.put(beanName, getBean(beanName, requiredType, args)); - } - String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); - if (primaryCandidate != null) { - return getBean(primaryCandidate, requiredType, args); - } - String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); - if (priorityCandidate != null) { - return getBean(priorityCandidate, requiredType, args); - } - throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); - } - else if (getParentBeanFactory() != null) { - return getParentBeanFactory().getBean(requiredType, args); + NamedBeanHolder namedBean = resolveNamedBean(requiredType, args); + if (namedBean != null) { + return namedBean.getBeanInstance(); } - else { - throw new NoSuchBeanDefinitionException(requiredType); + BeanFactory parent = getParentBeanFactory(); + if (parent != null) { + return parent.getBean(requiredType, args); } + throw new NoSuchBeanDefinitionException(requiredType); } @@ -642,13 +615,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto else if (containsSingleton(beanName)) { return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver); } - else if (getParentBeanFactory() instanceof DefaultListableBeanFactory) { + + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof DefaultListableBeanFactory) { // No bean definition found in this factory -> delegate to parent. - return ((DefaultListableBeanFactory) getParentBeanFactory()).isAutowireCandidate(beanName, descriptor, resolver); + return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver); } - else if (getParentBeanFactory() instanceof ConfigurableListableBeanFactory) { + else if (parent instanceof ConfigurableListableBeanFactory) { // If no DefaultListableBeanFactory, can't pass the resolver along. - return ((ConfigurableListableBeanFactory) getParentBeanFactory()).isAutowireCandidate(beanName, descriptor); + return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor); } else { return true; @@ -989,24 +964,74 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto //--------------------------------------------------------------------- @Override - public Object resolveDependency(DependencyDescriptor descriptor, String beanName, + public NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException { + NamedBeanHolder namedBean = resolveNamedBean(requiredType, (Object[]) null); + if (namedBean != null) { + return namedBean; + } + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof AutowireCapableBeanFactory) { + return ((AutowireCapableBeanFactory) parent).resolveNamedBean(requiredType); + } + return null; + } + + private NamedBeanHolder resolveNamedBean(Class requiredType, Object... args) throws BeansException { + Assert.notNull(requiredType, "Required type must not be null"); + String[] beanNames = getBeanNamesForType(requiredType); + if (beanNames.length > 1) { + ArrayList autowireCandidates = new ArrayList<>(); + for (String beanName : beanNames) { + if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { + autowireCandidates.add(beanName); + } + } + if (!autowireCandidates.isEmpty()) { + beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); + } + } + if (beanNames.length == 1) { + String beanName = beanNames[0]; + return new NamedBeanHolder<>(beanName, getBean(beanName, requiredType, args)); + } + else if (beanNames.length > 1) { + Map candidates = new LinkedHashMap<>(); + for (String beanName : beanNames) { + candidates.put(beanName, getBean(beanName, requiredType, args)); + } + String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); + if (primaryCandidate != null) { + return new NamedBeanHolder<>(primaryCandidate, getBean(primaryCandidate, requiredType, args)); + } + String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); + if (priorityCandidate != null) { + return new NamedBeanHolder<>(priorityCandidate, getBean(priorityCandidate, requiredType, args)); + } + throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); + } + return null; + } + + @Override + public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (Optional.class == descriptor.getDependencyType()) { - return createOptionalDependency(descriptor, beanName); + return createOptionalDependency(descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { - return new DependencyObjectProvider(descriptor, beanName); + return new DependencyObjectProvider(descriptor, requestingBeanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { - return new Jsr330ProviderFactory().createDependencyProvider(descriptor, beanName); + return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName); } else { - Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName); + Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( + descriptor, requestingBeanName); if (result == null) { - result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); + result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } @@ -1337,9 +1362,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto if (containsBeanDefinition(beanName)) { return getMergedLocalBeanDefinition(beanName).isPrimary(); } - BeanFactory parentFactory = getParentBeanFactory(); - return (parentFactory instanceof DefaultListableBeanFactory && - ((DefaultListableBeanFactory) parentFactory).isPrimary(beanName, beanInstance)); + BeanFactory parent = getParentBeanFactory(); + return (parent instanceof DefaultListableBeanFactory && + ((DefaultListableBeanFactory) parent).isPrimary(beanName, beanInstance)); } /** @@ -1415,8 +1440,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } - if (getParentBeanFactory() instanceof DefaultListableBeanFactory) { - ((DefaultListableBeanFactory) getParentBeanFactory()).checkBeanNotOfRequiredType(type, descriptor); + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof DefaultListableBeanFactory) { + ((DefaultListableBeanFactory) parent).checkBeanNotOfRequiredType(type, descriptor); } } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java index 47a89cc7df2..df0dad47658 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java @@ -41,14 +41,14 @@ import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.InjectionMetadata; import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.beans.factory.config.NamedBeanHolder; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.BridgeMethodResolver; @@ -569,21 +569,16 @@ public class PersistenceAnnotationBeanPostProcessor protected EntityManagerFactory findDefaultEntityManagerFactory(String requestingBeanName) throws NoSuchBeanDefinitionException { - String[] beanNames = - BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, EntityManagerFactory.class); - if (beanNames.length == 1) { - String unitName = beanNames[0]; - EntityManagerFactory emf = (EntityManagerFactory) this.beanFactory.getBean(unitName); - if (this.beanFactory instanceof ConfigurableBeanFactory) { - ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName); - } - return emf; - } - else if (beanNames.length > 1) { - throw new NoUniqueBeanDefinitionException(EntityManagerFactory.class, beanNames); + if (this.beanFactory instanceof ConfigurableListableBeanFactory) { + // Fancy variant with dependency registration + ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory; + NamedBeanHolder emfHolder = clbf.resolveNamedBean(EntityManagerFactory.class); + clbf.registerDependentBean(emfHolder.getBeanName(), requestingBeanName); + return emfHolder.getBeanInstance(); } else { - throw new NoSuchBeanDefinitionException(EntityManagerFactory.class); + // Plain variant: just find a default bean + return this.beanFactory.getBean(EntityManagerFactory.class); } } 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 600c8e921e3..972515686a0 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 @@ -31,6 +31,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.config.NamedBeanHolder; import org.springframework.beans.factory.support.StaticListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -393,12 +394,17 @@ class StubWebApplicationContext implements WebApplicationContext { } @Override - public Object resolveDependency(DependencyDescriptor descriptor, String beanName) { + public NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException { throw new UnsupportedOperationException("Dependency resolution not supported"); } @Override - public Object resolveDependency(DependencyDescriptor descriptor, String beanName, + public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) { + throw new UnsupportedOperationException("Dependency resolution not supported"); + } + + @Override + public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, TypeConverter typeConverter) { throw new UnsupportedOperationException("Dependency resolution not supported"); }