Programmatic ObjectProvider retrieval through BeanFactory API
Introduces getBeanProvider(Class) and getBeanProvider(ResolvableType), also narrowing getBean(String, Class) and isTypeMatch(String, Class) to a non-null Class argument and enriching NoUniqueBeanDefinitionException with a full ResolvableType. In addition, ObjectProvider supports iterable/stream access for collection-style resolution of multiple matching beans now, and collection injection falls back to an empty collection in a single-constructor case with non-null arguments. Issue: SPR-17075 Issue: SPR-11419 Issue: SPR-15338
This commit is contained in:
parent
82194f4ee0
commit
1603c4ab2f
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
|
@ -148,16 +148,13 @@ public interface BeanFactory {
|
|||
* <p>Translates aliases back to the corresponding canonical bean name.
|
||||
* Will ask the parent factory if the bean cannot be found in this factory instance.
|
||||
* @param name the name of the bean to retrieve
|
||||
* @param requiredType type the bean must match. Can be an interface or superclass
|
||||
* of the actual class, or {@code null} for any match. For example, if the value
|
||||
* is {@code Object.class}, this method will succeed whatever the class of the
|
||||
* returned instance.
|
||||
* @param requiredType type the bean must match; can be an interface or superclass
|
||||
* @return an instance of the bean
|
||||
* @throws NoSuchBeanDefinitionException if there is no such bean definition
|
||||
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
|
||||
* @throws BeansException if the bean could not be created
|
||||
*/
|
||||
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
|
||||
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
|
||||
|
||||
/**
|
||||
* Return an instance, which may be shared or independent, of the specified bean.
|
||||
|
@ -181,8 +178,7 @@ public interface BeanFactory {
|
|||
* 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.
|
||||
* @param requiredType type the bean must match; can be an interface or superclass
|
||||
* @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
|
||||
|
@ -200,8 +196,7 @@ public interface BeanFactory {
|
|||
* 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.
|
||||
* @param requiredType type the bean must match; can be an interface or superclass
|
||||
* @param args arguments to use when creating a bean instance using explicit arguments
|
||||
* (only applied when creating a new instance as opposed to retrieving an existing one)
|
||||
* @return an instance of the bean
|
||||
|
@ -213,6 +208,23 @@ public interface BeanFactory {
|
|||
*/
|
||||
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
|
||||
|
||||
/**
|
||||
* Return an provider for the specified bean, allowing for lazy on-demand retrieval
|
||||
* of instances, including availability and uniqueness options.
|
||||
* @param requiredType type the bean must match; can be an interface or superclass
|
||||
* @return a corresponding provider handle
|
||||
* @since 5.1
|
||||
*/
|
||||
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
|
||||
|
||||
/**
|
||||
* Return an provider for the specified bean, allowing for lazy on-demand retrieval
|
||||
* of instances, including availability and uniqueness options.
|
||||
* @param requiredType type the bean must match; can be a generic type declaration
|
||||
* @return a corresponding provider handle
|
||||
* @since 5.1
|
||||
*/
|
||||
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
|
||||
|
||||
/**
|
||||
* Does this bean factory contain a bean definition or externally registered singleton
|
||||
|
@ -298,7 +310,7 @@ public interface BeanFactory {
|
|||
* @see #getBean
|
||||
* @see #getType
|
||||
*/
|
||||
boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
|
||||
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
|
||||
|
||||
/**
|
||||
* Determine the type of the bean with the given name. More specifically,
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.beans.factory;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
@ -72,6 +73,29 @@ public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionExcepti
|
|||
this(type, Arrays.asList(beanNamesFound));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code NoUniqueBeanDefinitionException}.
|
||||
* @param type required type of the non-unique bean
|
||||
* @param beanNamesFound the names of all matching beans (as a Collection)
|
||||
* @since 5.1
|
||||
*/
|
||||
public NoUniqueBeanDefinitionException(ResolvableType type, Collection<String> beanNamesFound) {
|
||||
super(type, "expected single matching bean but found " + beanNamesFound.size() + ": " +
|
||||
StringUtils.collectionToCommaDelimitedString(beanNamesFound));
|
||||
this.numberOfBeansFound = beanNamesFound.size();
|
||||
this.beanNamesFound = beanNamesFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code NoUniqueBeanDefinitionException}.
|
||||
* @param type required type of the non-unique bean
|
||||
* @param beanNamesFound the names of all matching beans (as an array)
|
||||
* @since 5.1
|
||||
*/
|
||||
public NoUniqueBeanDefinitionException(ResolvableType type, String... beanNamesFound) {
|
||||
this(type, Arrays.asList(beanNamesFound));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the number of beans found when only one matching bean was expected.
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -26,11 +28,15 @@ import org.springframework.lang.Nullable;
|
|||
* A variant of {@link ObjectFactory} designed specifically for injection points,
|
||||
* allowing for programmatic optionality and lenient not-unique handling.
|
||||
*
|
||||
* <p>As of 5.1, this interface extends {@link Iterable} and provides {@link Stream}
|
||||
* support. It can be therefore be used in {@code for} loops, provides {@link #forEach}
|
||||
* iteration and allows for collection-style {@link #stream} access.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.3
|
||||
* @param <T> the object type
|
||||
*/
|
||||
public interface ObjectProvider<T> extends ObjectFactory<T> {
|
||||
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
|
@ -130,4 +136,27 @@ public interface ObjectProvider<T> extends ObjectFactory<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link Iterator} over resolved object instances.
|
||||
* <p>The default implementation delegates to {@link #stream()}.
|
||||
* @since 5.1
|
||||
* @see #stream()
|
||||
*/
|
||||
@Override
|
||||
default Iterator<T> iterator() {
|
||||
return stream().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sequential {@link Stream} over resolved object instances.
|
||||
* <p>The default implementation returns a stream of one element or an
|
||||
* empty stream if not available, resolved via {@link #getIfAvailable()}.
|
||||
* @since 5.1
|
||||
* @see #iterator()
|
||||
*/
|
||||
default Stream<T> stream() {
|
||||
T instance = getIfAvailable();
|
||||
return (instance != null ? Stream.of(instance) : Stream.empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -199,6 +199,41 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||
return this.eager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this descriptor allows for stream-style access to
|
||||
* result instances.
|
||||
* <p>By default, dependencies are strictly resolved to the declaration of
|
||||
* the injection point and therefore only resolve multiple entries if the
|
||||
* injection point is declared as an array, collection or map. This is
|
||||
* indicated by returning {@code false} here.
|
||||
* <p>Overriding this method to return {@code true} indicates that the
|
||||
* injection point declares the bean type but the resolution is meant to
|
||||
* end up in a {@link java.util.stream.Stream} for the declared bean type,
|
||||
* with the caller handling the multi-instance case for the injection point.
|
||||
* @since 5.1
|
||||
*/
|
||||
public boolean isStreamAccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified not-unique scenario: by default,
|
||||
* throwing a {@link NoUniqueBeanDefinitionException}.
|
||||
* <p>Subclasses may override this to select one of the instances or
|
||||
* to opt out with no result at all through returning {@code null}.
|
||||
* @param type the requested bean type
|
||||
* @param matchingBeans a map of bean names and corresponding bean
|
||||
* instances which have been pre-selected for the given type
|
||||
* (qualifiers etc already applied)
|
||||
* @return a bean instance to proceed with, or {@code null} for none
|
||||
* @throws BeansException in case of the not-unique scenario being fatal
|
||||
* @since 5.1
|
||||
*/
|
||||
@Nullable
|
||||
public Object resolveNotUnique(ResolvableType type, Map<String, Object> matchingBeans) throws BeansException {
|
||||
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified not-unique scenario: by default,
|
||||
* throwing a {@link NoUniqueBeanDefinitionException}.
|
||||
|
@ -211,7 +246,9 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||
* @return a bean instance to proceed with, or {@code null} for none
|
||||
* @throws BeansException in case of the not-unique scenario being fatal
|
||||
* @since 4.3
|
||||
* @deprecated as of 5.1, in favor of {@link #resolveNotUnique(ResolvableType, Map)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans) throws BeansException {
|
||||
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
|
||||
|
|
|
@ -200,7 +200,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
return doGetBean(name, requiredType, null, false);
|
||||
}
|
||||
|
||||
|
@ -277,10 +277,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
// Delegation to parent with explicit args.
|
||||
return (T) parentBeanFactory.getBean(nameToLookup, args);
|
||||
}
|
||||
else {
|
||||
else if (requiredType != null) {
|
||||
// No args -> delegate to standard getBean method.
|
||||
return parentBeanFactory.getBean(nameToLookup, requiredType);
|
||||
}
|
||||
else {
|
||||
return (T) parentBeanFactory.getBean(nameToLookup);
|
||||
}
|
||||
}
|
||||
|
||||
if (!typeCheckOnly) {
|
||||
|
@ -586,7 +589,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,12 @@ import org.springframework.beans.TypeMismatchException;
|
|||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.InjectionPoint;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
|
@ -133,7 +135,7 @@ class ConstructorResolver {
|
|||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +197,7 @@ class ConstructorResolver {
|
|||
}
|
||||
}
|
||||
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
|
||||
getUserDeclaredConstructor(candidate), autowiring);
|
||||
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
|
||||
}
|
||||
catch (UnsatisfiedDependencyException ex) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
@ -407,7 +409,7 @@ class ConstructorResolver {
|
|||
}
|
||||
}
|
||||
if (argsToResolve != null) {
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
|
||||
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -471,8 +473,8 @@ class ConstructorResolver {
|
|||
if (pnd != null) {
|
||||
paramNames = pnd.getParameterNames(candidate);
|
||||
}
|
||||
argsHolder = createArgumentArray(
|
||||
beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
|
||||
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
|
||||
paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
|
||||
}
|
||||
catch (UnsatisfiedDependencyException ex) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
|
@ -654,7 +656,7 @@ class ConstructorResolver {
|
|||
private ArgumentsHolder createArgumentArray(
|
||||
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
|
||||
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
|
||||
boolean autowiring) throws UnsatisfiedDependencyException {
|
||||
boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
|
||||
|
||||
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
|
||||
TypeConverter converter = (customConverter != null ? customConverter : bw);
|
||||
|
@ -720,8 +722,8 @@ class ConstructorResolver {
|
|||
"] - did you specify the correct bean references as arguments?");
|
||||
}
|
||||
try {
|
||||
Object autowiredArgument =
|
||||
resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter);
|
||||
Object autowiredArgument = resolveAutowiredArgument(
|
||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
||||
args.rawArguments[paramIndex] = autowiredArgument;
|
||||
args.arguments[paramIndex] = autowiredArgument;
|
||||
args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
|
||||
|
@ -749,8 +751,8 @@ class ConstructorResolver {
|
|||
/**
|
||||
* Resolve the prepared arguments stored in the given bean definition.
|
||||
*/
|
||||
private Object[] resolvePreparedArguments(
|
||||
String beanName, RootBeanDefinition mbd, BeanWrapper bw, Executable executable, Object[] argsToResolve) {
|
||||
private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
|
||||
Executable executable, Object[] argsToResolve, boolean fallback) {
|
||||
|
||||
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
|
||||
TypeConverter converter = (customConverter != null ? customConverter : bw);
|
||||
|
@ -764,7 +766,7 @@ class ConstructorResolver {
|
|||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, executable.getDeclaringClass());
|
||||
if (argValue instanceof AutowiredArgumentMarker) {
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter);
|
||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, fallback);
|
||||
}
|
||||
else if (argValue instanceof BeanMetadataElement) {
|
||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||
|
@ -806,17 +808,33 @@ class ConstructorResolver {
|
|||
*/
|
||||
@Nullable
|
||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter) {
|
||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||
|
||||
if (InjectionPoint.class.isAssignableFrom(param.getParameterType())) {
|
||||
Class<?> paramType = param.getParameterType();
|
||||
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
||||
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);
|
||||
try {
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
if (fallback) {
|
||||
// Single constructor or factory method -> let's return an
|
||||
// empty collection for a non-null collection parameter.
|
||||
if (CollectionFactory.isApproximableCollectionType(paramType)) {
|
||||
return CollectionFactory.createCollection(paramType, 0);
|
||||
}
|
||||
else if (CollectionFactory.isApproximableMapType(paramType)) {
|
||||
return CollectionFactory.createMap(paramType, 0);
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
|
||||
|
|
|
@ -40,6 +40,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
@ -333,17 +334,80 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
return getBean(requiredType, (Object[]) null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
|
||||
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args);
|
||||
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
|
||||
if (resolved == null) {
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
}
|
||||
return (T) resolved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) throws BeansException {
|
||||
return getBeanProvider(ResolvableType.forRawClass(requiredType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
return new BeanObjectProvider<T>() {
|
||||
@Override
|
||||
public T getObject() throws BeansException {
|
||||
T resolved = resolveBean(requiredType, null, false);
|
||||
if (resolved == null) {
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
@Override
|
||||
public T getObject(Object... args) throws BeansException {
|
||||
T resolved = resolveBean(requiredType, args, false);
|
||||
if (resolved == null) {
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfAvailable() throws BeansException {
|
||||
return resolveBean(requiredType, null, false);
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfUnique() throws BeansException {
|
||||
return resolveBean(requiredType, null, true);
|
||||
}
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return Arrays.stream(getBeanNamesForType(requiredType))
|
||||
.map(name -> (T) getBean(name))
|
||||
.filter(bean -> !(bean instanceof NullBean));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
|
||||
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
|
||||
if (namedBean != null) {
|
||||
return namedBean.getBeanInstance();
|
||||
}
|
||||
BeanFactory parent = getParentBeanFactory();
|
||||
if (parent != null) {
|
||||
return (args != null ? parent.getBean(requiredType, args) : parent.getBean(requiredType));
|
||||
if (parent instanceof DefaultListableBeanFactory) {
|
||||
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
|
||||
}
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
else if (parent != null) {
|
||||
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
|
||||
if (args != null) {
|
||||
return parentProvider.getObject(args);
|
||||
}
|
||||
else {
|
||||
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -978,7 +1042,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
|
||||
@Override
|
||||
public <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException {
|
||||
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, (Object[]) null);
|
||||
NamedBeanHolder<T> namedBean = resolveNamedBean(ResolvableType.forRawClass(requiredType), null, false);
|
||||
if (namedBean != null) {
|
||||
return namedBean;
|
||||
}
|
||||
|
@ -991,8 +1055,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
|
||||
private <T> NamedBeanHolder<T> resolveNamedBean(
|
||||
ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
|
||||
|
||||
Assert.notNull(requiredType, "Required type must not be null");
|
||||
Class<?> clazz = requiredType.getRawClass();
|
||||
Assert.notNull(clazz, "Required type must have a raw Class");
|
||||
String[] candidateNames = getBeanNamesForType(requiredType);
|
||||
|
||||
if (candidateNames.length > 1) {
|
||||
|
@ -1009,7 +1077,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
|
||||
if (candidateNames.length == 1) {
|
||||
String beanName = candidateNames[0];
|
||||
return new NamedBeanHolder<>(beanName, getBean(beanName, requiredType, args));
|
||||
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, clazz, args));
|
||||
}
|
||||
else if (candidateNames.length > 1) {
|
||||
Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
|
||||
|
@ -1022,18 +1090,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
candidates.put(beanName, getType(beanName));
|
||||
}
|
||||
}
|
||||
String candidateName = determinePrimaryCandidate(candidates, requiredType);
|
||||
String candidateName = determinePrimaryCandidate(candidates, clazz);
|
||||
if (candidateName == null) {
|
||||
candidateName = determineHighestPriorityCandidate(candidates, requiredType);
|
||||
candidateName = determineHighestPriorityCandidate(candidates, clazz);
|
||||
}
|
||||
if (candidateName != null) {
|
||||
Object beanInstance = candidates.get(candidateName);
|
||||
if (beanInstance == null || beanInstance instanceof Class) {
|
||||
beanInstance = getBean(candidateName, requiredType, args);
|
||||
beanInstance = getBean(candidateName, clazz, args);
|
||||
}
|
||||
return new NamedBeanHolder<>(candidateName, (T) beanInstance);
|
||||
}
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
|
||||
if (!nonUniqueAsNull) {
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -1110,7 +1180,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
|
||||
if (autowiredBeanName == null) {
|
||||
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
|
||||
return descriptor.resolveNotUnique(type, matchingBeans);
|
||||
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
|
||||
}
|
||||
else {
|
||||
// In case of an optional Collection/Map, silently ignore a non-unique case:
|
||||
|
@ -1156,7 +1226,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
|
||||
|
||||
Class<?> type = descriptor.getDependencyType();
|
||||
if (type.isArray()) {
|
||||
|
||||
if (descriptor.isStreamAccess()) {
|
||||
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type,
|
||||
new MultiElementDescriptor(descriptor));
|
||||
if (autowiredBeanNames != null) {
|
||||
autowiredBeanNames.addAll(matchingBeans.keySet());
|
||||
}
|
||||
return matchingBeans.values().stream();
|
||||
}
|
||||
else if (type.isArray()) {
|
||||
Class<?> componentType = type.getComponentType();
|
||||
ResolvableType resolvableType = descriptor.getResolvableType();
|
||||
Class<?> resolvedArrayType = resolvableType.resolve();
|
||||
|
@ -1645,10 +1724,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
}
|
||||
|
||||
|
||||
private interface BeanObjectProvider<T> extends ObjectProvider<T>, Serializable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serializable ObjectFactory/ObjectProvider for lazy resolution of a dependency.
|
||||
*/
|
||||
private class DependencyObjectProvider implements ObjectProvider<Object>, Serializable {
|
||||
private class DependencyObjectProvider implements BeanObjectProvider<Object> {
|
||||
|
||||
private final DependencyDescriptor descriptor;
|
||||
|
||||
|
@ -1724,7 +1807,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans) {
|
||||
public Object resolveNotUnique(ResolvableType type, Map<String, Object> matchingBeans) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
@ -1745,6 +1828,27 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
return doResolveDependency(this.descriptor, this.beanName, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Stream<Object> stream() {
|
||||
DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
|
||||
@Override
|
||||
public boolean isStreamAccess() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
|
||||
if (result instanceof Stream) {
|
||||
return (Stream<Object>) result;
|
||||
}
|
||||
else if (result instanceof Collection) {
|
||||
return ((Collection<Object>) result).stream();
|
||||
}
|
||||
else {
|
||||
return (result != null ? Stream.of(result) : Stream.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@ package org.springframework.beans.factory.support;
|
|||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
@ -31,6 +33,7 @@ import org.springframework.beans.factory.FactoryBean;
|
|||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.SmartFactoryBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
@ -178,6 +181,73 @@ public class StaticListableBeanFactory implements ListableBeanFactory {
|
|||
return getBean(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) throws BeansException {
|
||||
return getBeanProvider(ResolvableType.forRawClass(requiredType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
return new ObjectProvider<T>() {
|
||||
@Override
|
||||
public T getObject() throws BeansException {
|
||||
String[] beanNames = getBeanNamesForType(requiredType);
|
||||
if (beanNames.length == 1) {
|
||||
return (T) getBean(beanNames[0], requiredType);
|
||||
}
|
||||
else if (beanNames.length > 1) {
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
|
||||
}
|
||||
else {
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public T getObject(Object... args) throws BeansException {
|
||||
String[] beanNames = getBeanNamesForType(requiredType);
|
||||
if (beanNames.length == 1) {
|
||||
return (T) getBean(beanNames[0], args);
|
||||
}
|
||||
else if (beanNames.length > 1) {
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
|
||||
}
|
||||
else {
|
||||
throw new NoSuchBeanDefinitionException(requiredType);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfAvailable() throws BeansException {
|
||||
String[] beanNames = getBeanNamesForType(requiredType);
|
||||
if (beanNames.length == 1) {
|
||||
return (T) getBean(beanNames[0]);
|
||||
}
|
||||
else if (beanNames.length > 1) {
|
||||
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfUnique() throws BeansException {
|
||||
String[] beanNames = getBeanNamesForType(requiredType);
|
||||
if (beanNames.length == 1) {
|
||||
return (T) getBean(beanNames[0]);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
return this.beans.containsKey(name);
|
||||
|
|
|
@ -27,14 +27,17 @@ import java.security.PrivilegedAction;
|
|||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Priority;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
|
@ -95,6 +98,7 @@ import org.springframework.tests.sample.beans.NestedTestBean;
|
|||
import org.springframework.tests.sample.beans.SideEffectBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
import org.springframework.tests.sample.beans.factory.DummyFactory;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
|
@ -1559,10 +1563,42 @@ public class DefaultListableBeanFactoryTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchBeanDefinitionException.class)
|
||||
@Test
|
||||
public void testGetBeanByTypeInstanceWithNoneFound() {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
lbf.getBean(ConstructorDependency.class, 42);
|
||||
|
||||
try {
|
||||
lbf.getBean(ConstructorDependency.class);
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
lbf.getBean(ConstructorDependency.class, 42);
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
ObjectProvider<ConstructorDependency> provider = lbf.getBeanProvider(ConstructorDependency.class);
|
||||
try {
|
||||
provider.getObject();
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
provider.getObject(42);
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
assertNull(provider.getIfAvailable());
|
||||
assertNull(provider.getIfUnique());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1571,9 +1607,27 @@ public class DefaultListableBeanFactoryTests {
|
|||
RootBeanDefinition bd1 = createConstructorDependencyBeanDefinition(99);
|
||||
parent.registerBeanDefinition("bd1", bd1);
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(parent);
|
||||
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class, 42);
|
||||
|
||||
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class);
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(99));
|
||||
bean = lbf.getBean(ConstructorDependency.class, 42);
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(42));
|
||||
|
||||
ObjectProvider<ConstructorDependency> provider = lbf.getBeanProvider(ConstructorDependency.class);
|
||||
bean = provider.getObject();
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(99));
|
||||
bean = provider.getObject(42);
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(42));
|
||||
bean = provider.getIfAvailable();
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(99));
|
||||
bean = provider.getIfUnique();
|
||||
assertThat(bean.beanName, equalTo("bd1"));
|
||||
assertThat(bean.spouseAge, equalTo(99));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1583,12 +1637,66 @@ public class DefaultListableBeanFactoryTests {
|
|||
RootBeanDefinition bd2 = new RootBeanDefinition(ConstructorDependency.class);
|
||||
bd2.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bd2.getConstructorArgumentValues().addGenericArgumentValue("43");
|
||||
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
|
||||
thrown.expect(NoUniqueBeanDefinitionException.class);
|
||||
lbf.getBean(ConstructorDependency.class, 42);
|
||||
try {
|
||||
lbf.getBean(ConstructorDependency.class);
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
lbf.getBean(ConstructorDependency.class, 42);
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
|
||||
ObjectProvider<ConstructorDependency> provider = lbf.getBeanProvider(ConstructorDependency.class);
|
||||
try {
|
||||
provider.getObject();
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
provider.getObject(42);
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
provider.getIfAvailable();
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
assertNull(provider.getIfUnique());
|
||||
|
||||
Set<Object> resolved = new HashSet<>();
|
||||
for (ConstructorDependency instance : provider) {
|
||||
resolved.add(instance);
|
||||
}
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
|
||||
resolved = new HashSet<>();
|
||||
provider.forEach(resolved::add);
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
|
||||
resolved = provider.stream().collect(Collectors.toSet());
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1599,9 +1707,46 @@ public class DefaultListableBeanFactoryTests {
|
|||
bd2.setPrimary(true);
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class, 42);
|
||||
|
||||
ConstructorDependency bean = lbf.getBean(ConstructorDependency.class);
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(43));
|
||||
bean = lbf.getBean(ConstructorDependency.class, 42);
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(42));
|
||||
|
||||
ObjectProvider<ConstructorDependency> provider = lbf.getBeanProvider(ConstructorDependency.class);
|
||||
bean = provider.getObject();
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(43));
|
||||
bean = provider.getObject(42);
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(42));
|
||||
bean = provider.getIfAvailable();
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(43));
|
||||
bean = provider.getIfUnique();
|
||||
assertThat(bean.beanName, equalTo("bd2"));
|
||||
assertThat(bean.spouseAge, equalTo(43));
|
||||
|
||||
Set<Object> resolved = new HashSet<>();
|
||||
for (ConstructorDependency instance : provider) {
|
||||
resolved.add(instance);
|
||||
}
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
|
||||
resolved = new HashSet<>();
|
||||
provider.forEach(resolved::add);
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
|
||||
resolved = provider.stream().collect(Collectors.toSet());
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(lbf.getBean("bd1")));
|
||||
assertTrue(resolved.contains(lbf.getBean("bd2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1611,9 +1756,9 @@ public class DefaultListableBeanFactoryTests {
|
|||
RootBeanDefinition bd2 = createConstructorDependencyBeanDefinition(43);
|
||||
bd1.setPrimary(true);
|
||||
bd2.setPrimary(true);
|
||||
|
||||
lbf.registerBeanDefinition("bd1", bd1);
|
||||
lbf.registerBeanDefinition("bd2", bd2);
|
||||
|
||||
thrown.expect(NoUniqueBeanDefinitionException.class);
|
||||
thrown.expectMessage(containsString("more than one 'primary'"));
|
||||
lbf.getBean(ConstructorDependency.class, 42);
|
||||
|
@ -1642,6 +1787,31 @@ public class DefaultListableBeanFactoryTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanProviderSerialization() throws Exception {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
lbf.setSerializationId("test");
|
||||
|
||||
ObjectProvider<ConstructorDependency> provider = lbf.getBeanProvider(ConstructorDependency.class);
|
||||
ObjectProvider deserialized = (ObjectProvider) SerializationTestUtils.serializeAndDeserialize(provider);
|
||||
try {
|
||||
deserialized.getObject();
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
deserialized.getObject(42);
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
assertNull(deserialized.getIfAvailable());
|
||||
assertNull(deserialized.getIfUnique());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeanWithArgsNotCreatedForFactoryBeanChecking() {
|
||||
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
|
||||
|
@ -2859,6 +3029,21 @@ public class DefaultListableBeanFactoryTests {
|
|||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ConstructorDependency that = (ConstructorDependency) o;
|
||||
return spouseAge == that.spouseAge &&
|
||||
Objects.equals(spouse, that.spouse) &&
|
||||
Objects.equals(beanName, that.beanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(spouse, spouseAge, beanName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Map;
|
|||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
@ -811,13 +812,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSingleConstructorInjectionWithMultipleCandidatesAsOrderedCollection() {
|
||||
public void testSingleConstructorInjectionWithMultipleCandidatesAsRequiredCollection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorRequiredCollectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
|
@ -825,7 +826,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
|
||||
SingleConstructorRequiredCollectionBean bean = (SingleConstructorRequiredCollectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().size());
|
||||
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||
|
@ -840,11 +841,52 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorRequiredCollectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
|
||||
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
|
||||
SingleConstructorRequiredCollectionBean bean = (SingleConstructorRequiredCollectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertNotNull(bean.getNestedTestBeans());
|
||||
assertTrue(bean.getNestedTestBeans().isEmpty());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleConstructorInjectionWithMultipleCandidatesAsOrderedCollection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
SingleConstructorOptionalCollectionBean bean = (SingleConstructorOptionalCollectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().size());
|
||||
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||
assertSame(ntb1, bean.getNestedTestBeans().get(1));
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleConstructorInjectionWithEmptyCollectionAsNull() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
|
||||
SingleConstructorOptionalCollectionBean bean = (SingleConstructorOptionalCollectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertNull(bean.getNestedTestBeans());
|
||||
bf.destroySingletons();
|
||||
|
@ -857,7 +899,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
|
||||
|
||||
bf.getBean("annotatedBean");
|
||||
}
|
||||
|
@ -869,7 +911,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorOptionalCollectionBean.class));
|
||||
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
@ -1333,17 +1375,17 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSmartObjectFactoryInjectionWithPrototype() {
|
||||
public void testObjectProviderInjectionWithPrototype() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
|
||||
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
|
||||
tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("testBean", tbd);
|
||||
|
||||
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
|
||||
assertEquals(bf.getBean("testBean"), bean.getTestBean());
|
||||
assertEquals(bf.getBean("testBean", "myName"), bean.getTestBean("myName"));
|
||||
assertEquals(bf.getBean("testBean"), bean.getOptionalTestBean());
|
||||
|
@ -1352,19 +1394,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
assertEquals(bf.getBean("testBean"), bean.getUniqueTestBean());
|
||||
assertEquals(bf.getBean("testBean"), bean.getUniqueTestBeanWithDefault());
|
||||
assertEquals(bf.getBean("testBean"), bean.consumeUniqueTestBean());
|
||||
|
||||
List<?> testBeans = bean.iterateTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
testBeans = bean.forEachTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
testBeans = bean.streamTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartObjectFactoryInjectionWithSingletonTarget() {
|
||||
public void testObjectProviderInjectionWithSingletonTarget() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
assertSame(bf.getBean("testBean"), bean.getOptionalTestBean());
|
||||
assertSame(bf.getBean("testBean"), bean.getOptionalTestBeanWithDefault());
|
||||
|
@ -1372,18 +1425,29 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
assertSame(bf.getBean("testBean"), bean.getUniqueTestBean());
|
||||
assertSame(bf.getBean("testBean"), bean.getUniqueTestBeanWithDefault());
|
||||
assertEquals(bf.getBean("testBean"), bean.consumeUniqueTestBean());
|
||||
|
||||
List<?> testBeans = bean.iterateTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
testBeans = bean.forEachTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
testBeans = bean.streamTestBeans();
|
||||
assertEquals(1, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean")));
|
||||
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartObjectFactoryInjectionWithTargetNotAvailable() {
|
||||
public void testObjectProviderInjectionWithTargetNotAvailable() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
|
||||
|
||||
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
|
||||
try {
|
||||
bean.getTestBean();
|
||||
fail("Should have thrown NoSuchBeanDefinitionException");
|
||||
|
@ -1397,20 +1461,28 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
assertEquals(new TestBean("default"), bean.getUniqueTestBeanWithDefault());
|
||||
assertNull(bean.getUniqueTestBean());
|
||||
assertNull(bean.consumeUniqueTestBean());
|
||||
|
||||
List<?> testBeans = bean.iterateTestBeans();
|
||||
assertTrue(testBeans.isEmpty());
|
||||
testBeans = bean.forEachTestBeans();
|
||||
assertTrue(testBeans.isEmpty());
|
||||
testBeans = bean.streamTestBeans();
|
||||
assertTrue(testBeans.isEmpty());
|
||||
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartObjectFactoryInjectionWithTargetNotUnique() {
|
||||
public void testObjectProviderInjectionWithTargetNotUnique() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class));
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
|
||||
try {
|
||||
bean.getTestBean();
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
|
@ -1434,16 +1506,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
assertNull(bean.getUniqueTestBean());
|
||||
assertNull(bean.consumeUniqueTestBean());
|
||||
|
||||
List<?> testBeans = bean.iterateTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
testBeans = bean.forEachTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
testBeans = bean.streamTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartObjectFactoryInjectionWithTargetPrimary() {
|
||||
public void testObjectProviderInjectionWithTargetPrimary() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectProviderInjectionBean.class));
|
||||
RootBeanDefinition tb1 = new RootBeanDefinition(TestBean.class);
|
||||
tb1.setPrimary(true);
|
||||
bf.registerBeanDefinition("testBean1", tb1);
|
||||
|
@ -1451,13 +1537,27 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
tb2.setLazyInit(true);
|
||||
bf.registerBeanDefinition("testBean2", tb2);
|
||||
|
||||
SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectProviderInjectionBean bean = (ObjectProviderInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean1"), bean.getTestBean());
|
||||
assertSame(bf.getBean("testBean1"), bean.getOptionalTestBean());
|
||||
assertSame(bf.getBean("testBean1"), bean.consumeOptionalTestBean());
|
||||
assertSame(bf.getBean("testBean1"), bean.getUniqueTestBean());
|
||||
assertSame(bf.getBean("testBean1"), bean.consumeUniqueTestBean());
|
||||
assertFalse(bf.containsSingleton("testBean2"));
|
||||
|
||||
List<?> testBeans = bean.iterateTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
testBeans = bean.forEachTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
testBeans = bean.streamTestBeans();
|
||||
assertEquals(2, testBeans.size());
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean1")));
|
||||
assertTrue(testBeans.contains(bf.getBean("testBean2")));
|
||||
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
|
@ -2941,13 +3041,34 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
|
||||
public static class SingleConstructorCollectionInjectionBean {
|
||||
public static class SingleConstructorRequiredCollectionBean {
|
||||
|
||||
private ITestBean testBean;
|
||||
|
||||
private List<NestedTestBean> nestedTestBeans;
|
||||
|
||||
public SingleConstructorCollectionInjectionBean(ITestBean testBean,
|
||||
public SingleConstructorRequiredCollectionBean(ITestBean testBean, List<NestedTestBean> nestedTestBeans) {
|
||||
this.testBean = testBean;
|
||||
this.nestedTestBeans = nestedTestBeans;
|
||||
}
|
||||
|
||||
public ITestBean getTestBean() {
|
||||
return this.testBean;
|
||||
}
|
||||
|
||||
public List<NestedTestBean> getNestedTestBeans() {
|
||||
return this.nestedTestBeans;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class SingleConstructorOptionalCollectionBean {
|
||||
|
||||
private ITestBean testBean;
|
||||
|
||||
private List<NestedTestBean> nestedTestBeans;
|
||||
|
||||
public SingleConstructorOptionalCollectionBean(ITestBean testBean,
|
||||
@Autowired(required = false) List<NestedTestBean> nestedTestBeans) {
|
||||
this.testBean = testBean;
|
||||
this.nestedTestBeans = nestedTestBeans;
|
||||
|
@ -3096,46 +3217,64 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
|
||||
public static class SmartObjectFactoryInjectionBean {
|
||||
public static class ObjectProviderInjectionBean {
|
||||
|
||||
@Autowired
|
||||
private ObjectProvider<TestBean> testBeanFactory;
|
||||
private ObjectProvider<TestBean> testBeanProvider;
|
||||
|
||||
private TestBean consumedTestBean;
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.getObject();
|
||||
return this.testBeanProvider.getObject();
|
||||
}
|
||||
|
||||
public TestBean getTestBean(String name) {
|
||||
return this.testBeanFactory.getObject(name);
|
||||
return this.testBeanProvider.getObject(name);
|
||||
}
|
||||
|
||||
public TestBean getOptionalTestBean() {
|
||||
return this.testBeanFactory.getIfAvailable();
|
||||
return this.testBeanProvider.getIfAvailable();
|
||||
}
|
||||
|
||||
public TestBean getOptionalTestBeanWithDefault() {
|
||||
return this.testBeanFactory.getIfAvailable(() -> new TestBean("default"));
|
||||
return this.testBeanProvider.getIfAvailable(() -> new TestBean("default"));
|
||||
}
|
||||
|
||||
public TestBean consumeOptionalTestBean() {
|
||||
this.testBeanFactory.ifAvailable(tb -> consumedTestBean = tb);
|
||||
this.testBeanProvider.ifAvailable(tb -> consumedTestBean = tb);
|
||||
return consumedTestBean;
|
||||
}
|
||||
|
||||
public TestBean getUniqueTestBean() {
|
||||
return this.testBeanFactory.getIfUnique();
|
||||
return this.testBeanProvider.getIfUnique();
|
||||
}
|
||||
|
||||
public TestBean getUniqueTestBeanWithDefault() {
|
||||
return this.testBeanFactory.getIfUnique(() -> new TestBean("default"));
|
||||
return this.testBeanProvider.getIfUnique(() -> new TestBean("default"));
|
||||
}
|
||||
|
||||
public TestBean consumeUniqueTestBean() {
|
||||
this.testBeanFactory.ifUnique(tb -> consumedTestBean = tb);
|
||||
this.testBeanProvider.ifUnique(tb -> consumedTestBean = tb);
|
||||
return consumedTestBean;
|
||||
}
|
||||
|
||||
public List<TestBean> iterateTestBeans() {
|
||||
List<TestBean> resolved = new LinkedList<>();
|
||||
for (TestBean tb : this.testBeanProvider) {
|
||||
resolved.add(tb);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public List<TestBean> forEachTestBeans() {
|
||||
List<TestBean> resolved = new LinkedList<>();
|
||||
this.testBeanProvider.forEach(resolved::add);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public List<TestBean> streamTestBeans() {
|
||||
return this.testBeanProvider.stream().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
@ -37,6 +38,8 @@ import org.mockito.Mockito;
|
|||
import org.springframework.beans.PropertyEditorRegistrar;
|
||||
import org.springframework.beans.PropertyEditorRegistry;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||
|
@ -160,7 +163,7 @@ public class BeanFactoryGenericsTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGenericListOfArraysProperty() throws MalformedURLException {
|
||||
public void testGenericListOfArraysProperty() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(
|
||||
new ClassPathResource("genericBeanTests.xml", getClass()));
|
||||
|
@ -863,6 +866,66 @@ public class BeanFactoryGenericsTests {
|
|||
assertEquals("store1", doubleStoreNames[0]);
|
||||
assertEquals(1, floatStoreNames.length);
|
||||
assertEquals("store2", floatStoreNames[0]);
|
||||
|
||||
ObjectProvider<NumberStore<?>> numberStoreProvider = bf.getBeanProvider(ResolvableType.forClass(NumberStore.class));
|
||||
ObjectProvider<NumberStore<Double>> doubleStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Double.class));
|
||||
ObjectProvider<NumberStore<Float>> floatStoreProvider = bf.getBeanProvider(ResolvableType.forClassWithGenerics(NumberStore.class, Float.class));
|
||||
try {
|
||||
numberStoreProvider.getObject();
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
numberStoreProvider.getIfAvailable();
|
||||
fail("Should have thrown NoUniqueBeanDefinitionException");
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
// expected
|
||||
}
|
||||
assertNull(numberStoreProvider.getIfUnique());
|
||||
assertSame(bf.getBean("store1"), doubleStoreProvider.getObject());
|
||||
assertSame(bf.getBean("store1"), doubleStoreProvider.getIfAvailable());
|
||||
assertSame(bf.getBean("store1"), doubleStoreProvider.getIfUnique());
|
||||
assertSame(bf.getBean("store2"), floatStoreProvider.getObject());
|
||||
assertSame(bf.getBean("store2"), floatStoreProvider.getIfAvailable());
|
||||
assertSame(bf.getBean("store2"), floatStoreProvider.getIfUnique());
|
||||
|
||||
Set<Object> resolved = new HashSet<>();
|
||||
for (NumberStore<?> instance : numberStoreProvider) {
|
||||
resolved.add(instance);
|
||||
}
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store1")));
|
||||
assertTrue(resolved.contains(bf.getBean("store2")));
|
||||
|
||||
resolved = numberStoreProvider.stream().collect(Collectors.toSet());
|
||||
assertEquals(2, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store1")));
|
||||
assertTrue(resolved.contains(bf.getBean("store2")));
|
||||
|
||||
resolved = new HashSet<>();
|
||||
for (NumberStore<Double> instance : doubleStoreProvider) {
|
||||
resolved.add(instance);
|
||||
}
|
||||
assertEquals(1, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store1")));
|
||||
|
||||
resolved = doubleStoreProvider.stream().collect(Collectors.toSet());
|
||||
assertEquals(1, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store1")));
|
||||
|
||||
resolved = new HashSet<>();
|
||||
for (NumberStore<Float> instance : floatStoreProvider) {
|
||||
resolved.add(instance);
|
||||
}
|
||||
assertEquals(1, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store2")));
|
||||
|
||||
resolved = floatStoreProvider.stream().collect(Collectors.toSet());
|
||||
assertEquals(1, resolved.size());
|
||||
assertTrue(resolved.contains(bf.getBean("store2")));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.springframework.beans.BeansException;
|
|||
import org.springframework.beans.CachedIntrospectionResults;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
|
@ -1083,7 +1084,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBean(name, requiredType);
|
||||
}
|
||||
|
@ -1106,6 +1107,18 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
return getBeanFactory().getBean(requiredType, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
return getBeanFactory().containsBean(name);
|
||||
|
@ -1130,7 +1143,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
assertBeanFactoryActive();
|
||||
return getBeanFactory().isTypeMatch(name, typeToMatch);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import org.springframework.beans.factory.BeanDefinitionStoreException;
|
|||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.jndi.TypeMismatchNamingException;
|
||||
|
@ -107,7 +109,7 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
try {
|
||||
if (isSingleton(name)) {
|
||||
return doGetSingleton(name, requiredType);
|
||||
|
@ -150,6 +152,49 @@ public class SimpleJndiBeanFactory extends JndiLocatorSupport implements BeanFac
|
|||
return getBean(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
|
||||
return new ObjectProvider<T>() {
|
||||
@Override
|
||||
public T getObject() throws BeansException {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
@Override
|
||||
public T getObject(Object... args) throws BeansException {
|
||||
return getBean(requiredType, args);
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfAvailable() throws BeansException {
|
||||
try {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
@Nullable
|
||||
public T getIfUnique() throws BeansException {
|
||||
try {
|
||||
return getBean(requiredType);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
throw new UnsupportedOperationException(
|
||||
"SimpleJndiBeanFactory does not support resolution by ResolvableType");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
if (this.singletonObjects.containsKey(name) || this.resourceTypes.containsKey(name)) {
|
||||
|
|
|
@ -44,12 +44,6 @@ import static org.springframework.util.StringUtils.*;
|
|||
*/
|
||||
public class AnnotationConfigApplicationContextTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void nullGetBeanParameterIsDisallowed() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
context.getBean((Class<?>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scanAndRefresh() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
|
|
@ -801,6 +801,40 @@ public class ConfigurationClassPostProcessorTests {
|
|||
assertSame(ctx.getBean(BarImpl.class), ctx.getBean(FooImpl.class).bar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertEquals(1, bean.testBeans.size());
|
||||
assertSame(ctx.getBean(TestBean.class), bean.testBeans.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyCollectionArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
|
||||
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertTrue(bean.testBeans.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertEquals(1, bean.testBeans.size());
|
||||
assertSame(ctx.getBean(Runnable.class), bean.testBeans.values().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMapArgumentOnBeanMethod() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
|
||||
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
|
||||
assertNotNull(bean.testBeans);
|
||||
assertTrue(bean.testBeans.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectionInjectionFromSameConfigurationClass() {
|
||||
ApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
|
||||
|
@ -1564,6 +1598,43 @@ public class ConfigurationClassPostProcessorTests {
|
|||
}
|
||||
}
|
||||
|
||||
public static class DummyRunnable implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
/* no-op */
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CollectionArgumentConfiguration {
|
||||
|
||||
List<TestBean> testBeans;
|
||||
|
||||
@Bean(autowireCandidate = false)
|
||||
public TestBean thing(List<TestBean> testBeans) {
|
||||
this.testBeans = testBeans;
|
||||
return new TestBean();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class MapArgumentConfiguration {
|
||||
|
||||
Map<String, Runnable> testBeans;
|
||||
|
||||
@Bean(autowireCandidate = false)
|
||||
Runnable testBean(Map<String, Runnable> testBeans) {
|
||||
this.testBeans = testBeans;
|
||||
return () -> {};
|
||||
}
|
||||
|
||||
// Unrelated, not to be considered as a factory method
|
||||
private boolean testBean(boolean param) {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CollectionInjectionConfiguration {
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 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,7 @@ 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.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.NamedBeanHolder;
|
||||
|
@ -161,7 +162,7 @@ class StubWebApplicationContext implements WebApplicationContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
|
||||
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
|
||||
return this.beanFactory.getBean(name, requiredType);
|
||||
}
|
||||
|
||||
|
@ -180,6 +181,16 @@ class StubWebApplicationContext implements WebApplicationContext {
|
|||
return this.beanFactory.getBean(requiredType, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) {
|
||||
return this.beanFactory.getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
|
||||
return this.beanFactory.getBeanProvider(requiredType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsBean(String name) {
|
||||
return this.beanFactory.containsBean(name);
|
||||
|
@ -201,7 +212,7 @@ class StubWebApplicationContext implements WebApplicationContext {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||
return this.beanFactory.isTypeMatch(name, typeToMatch);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue