
infra.beans.factory.support.AbstractAutowireCapableBeanFactory Maven / Gradle / Ivy
/*
* Copyright 2017 - 2024 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see [https://www.gnu.org/licenses/]
*/
package infra.beans.factory.support;
import java.io.Serial;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
import infra.beans.BeanMetadata;
import infra.beans.BeanProperty;
import infra.beans.BeanUtils;
import infra.beans.BeanWrapper;
import infra.beans.BeanWrapperImpl;
import infra.beans.BeansException;
import infra.beans.PropertyAccessorUtils;
import infra.beans.PropertyValue;
import infra.beans.PropertyValues;
import infra.beans.TypeConverter;
import infra.beans.factory.Aware;
import infra.beans.factory.BeanClassLoaderAware;
import infra.beans.factory.BeanCreationException;
import infra.beans.factory.BeanCurrentlyInCreationException;
import infra.beans.factory.BeanDefinitionStoreException;
import infra.beans.factory.BeanDefinitionValidationException;
import infra.beans.factory.BeanFactory;
import infra.beans.factory.BeanFactoryAware;
import infra.beans.factory.BeanInitializationException;
import infra.beans.factory.BeanNameAware;
import infra.beans.factory.DependenciesBeanPostProcessor;
import infra.beans.factory.FactoryBean;
import infra.beans.factory.InitializationBeanPostProcessor;
import infra.beans.factory.InitializingBean;
import infra.beans.factory.InjectionPoint;
import infra.beans.factory.UnsatisfiedDependencyException;
import infra.beans.factory.config.AutowireCapableBeanFactory;
import infra.beans.factory.config.AutowiredPropertyMarker;
import infra.beans.factory.config.BeanDefinition;
import infra.beans.factory.config.ConfigurableBeanFactory;
import infra.beans.factory.config.ConstructorArgumentValues;
import infra.beans.factory.config.DependencyDescriptor;
import infra.beans.factory.config.InstantiationAwareBeanPostProcessor;
import infra.beans.factory.config.PropertyValueRetriever;
import infra.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import infra.beans.factory.config.TypedStringValue;
import infra.core.DefaultParameterNameDiscoverer;
import infra.core.MethodParameter;
import infra.core.NamedThreadLocal;
import infra.core.ParameterNameDiscoverer;
import infra.core.PriorityOrdered;
import infra.core.ResolvableType;
import infra.core.annotation.AnnotationAwareOrderComparator;
import infra.lang.NullValue;
import infra.lang.Nullable;
import infra.logging.Logger;
import infra.util.ClassUtils;
import infra.util.ObjectUtils;
import infra.util.ReflectionUtils;
import infra.util.StringUtils;
import infra.util.function.ThrowingSupplier;
/**
* Abstract bean factory superclass that implements default bean creation,
* with the full capabilities specified by the {@link BeanDefinition} class.
* Implements the {@link AutowireCapableBeanFactory} interface in addition
* to AbstractBeanFactory's {@link #createBean} method.
*
* Provides bean creation (with constructor resolution), property population,
* wiring (including autowiring), and initialization. Handles runtime bean
* references, resolves managed collections, calls initialization methods, etc.
* Supports autowiring constructors, properties by name, and properties by type.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Mark Fisher
* @author Costin Leau
* @author Chris Beams
* @author Sam Brannen
* @author Phillip Webb
* @author Harry Yang
* @see BeanDefinition
* @see BeanDefinitionRegistry
* @since 4.0 2021/10/1 23:06
*/
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
/** Whether to automatically try to resolve circular references between beans. @since 4.0 */
private boolean allowCircularReferences = true;
/**
* Whether to resort to injecting a raw bean instance in case of circular reference,
* even if the injected bean eventually got wrapped.
*
* @since 4.0
*/
private boolean allowRawInjectionDespiteWrapping = false;
/** Cache of unfinished FactoryBean instances: FactoryBean name to its instance. @since 4.0 */
private final ConcurrentHashMap factoryBeanInstanceCache = new ConcurrentHashMap<>();
/**
* Dependency types to ignore on dependency check and autowire, as Set of
* Class objects: for example, String. Default is none.
*
* @since 4.0
*/
private final HashSet> ignoredDependencyTypes = new HashSet<>();
/**
* Dependency interfaces to ignore on dependency check and autowire, as Set of
* Class objects. By default, only the BeanFactory interface is ignored.
*
* @since 4.0
*/
private final HashSet> ignoredDependencyInterfaces = new HashSet<>();
/** Resolver strategy for method parameter names. @since 4.0 */
@Nullable
private ParameterNameDiscoverer parameterNameDiscoverer = ParameterNameDiscoverer.getSharedInstance();
/** Strategy for creating bean instances. */
private InstantiationStrategy instantiationStrategy;
/** Cache of filtered BeanProperty: bean Class to PropertyDescriptor array. */
private final ConcurrentMap, BeanProperty[]> filteredBeanPropertiesCache =
new ConcurrentHashMap<>();
/**
* The name of the currently created bean, for implicit dependency registration
* on getBean etc invocations triggered from a user-specified Supplier callback.
*/
private final NamedThreadLocal currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");
/**
* Create a new AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
}
/**
* Create a new AbstractAutowireCapableBeanFactory with the given parent.
*
* @param parentBeanFactory parent bean factory, or {@code null} if none
*/
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
//---------------------------------------------------------------------
// Implementation of AutowireCapableBeanFactory interface
//---------------------------------------------------------------------
@Override
public T createBean(Class beanClass, int autowireMode) throws BeansException {
return createBean(beanClass, autowireMode, false);
}
@Override
@SuppressWarnings("unchecked")
public T createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
// Use non-singleton bean merged, to avoid registering bean as dependent bean.
RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
Object bean = createBean(beanClass.getName(), bd, null);
if (bean == NullValue.INSTANCE) {
return null;
}
return (T) bean;
}
@Override
public void autowireBean(Object existingBean) {
// Use non-singleton bean merged, to avoid registering bean as dependent bean.
RootBeanDefinition merged = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
merged.setScope(BeanDefinition.SCOPE_PROTOTYPE);
merged.allowCaching = ClassUtils.isCacheSafe(merged.getBeanClass(), getBeanClassLoader());
BeanWrapper bw = createBeanWrapper(merged, existingBean);
populateBean(merged.getBeanClass().getName(), merged, bw);
}
//---------------------------------------------------------------------
// Implementation of relevant AbstractBeanFactory template methods
//---------------------------------------------------------------------
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
*
* @see #doCreateBean
* @see NullValue#INSTANCE
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition merged, @Nullable Object[] args)
throws BeanCreationException {
if (log.isDebugEnabled()) {
log.trace("Creating instance of bean '{}'", beanName);
}
RootBeanDefinition mbdToUse = merged;
// Make sure bean class is actually resolved at this point, and
// clone the bean merged in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean merged.
Class> resolvedClass = resolveBeanClass(beanName, merged);
if (resolvedClass != null && !merged.hasBeanClass() && merged.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(merged);
mbdToUse.setBeanClass(resolvedClass);
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (log.isDebugEnabled()) {
log.trace("Finished creating instance of bean '{}'", beanName);
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"Unexpected exception during bean creation", ex);
}
}
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
*
* @param merged the merged bean merged for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean, may be a {@code NullValue.INSTANCE}
* @throws BeanCreationException if the bean could not be created
* @see NullValue#INSTANCE
*/
protected Object doCreateBean(String beanName, RootBeanDefinition merged, @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (merged.isSingleton()) {
instanceWrapper = factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, merged, args);
}
Object bean = instanceWrapper.getWrappedInstance();
if (bean == NullValue.INSTANCE) {
return bean;
}
Class> beanType = bean.getClass();
merged.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean merged.
synchronized(merged.postProcessingLock) {
if (!merged.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(merged, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(merged.getResourceDescription(), beanName,
"Post-processing of bean merged failed", ex);
}
merged.markAsPostProcessed();
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = isEarlySingletonExposure(merged, beanName);
if (earlySingletonExposure) {
if (log.isDebugEnabled()) {
log.trace("Eagerly caching bean '{}' to allow for resolving potential circular references", beanName);
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, merged, bean));
}
// Initialize the bean instance.
Object fullyInitializedBean;
try {
// apply properties
populateBean(beanName, merged, instanceWrapper);
// Initialize the bean instance.
fullyInitializedBean = initializeBean(bean, beanName, merged);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(merged.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (fullyInitializedBean == bean) {
fullyInitializedBean = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
LinkedHashSet actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName, """
Bean with name '%s' has been injected into other beans [%s] in its raw version \
as part of a circular reference, but has eventually been wrapped. This means \
that said other beans do not use the final version of the bean. This is often \
the result of over-eager type matching - consider using 'getBeanNamesForType' \
with the 'allowEagerInit' flag turned off, for example."""
.formatted(beanName, StringUtils.collectionToCommaDelimitedString(actualDependentBeans)));
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, merged);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
merged.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return fullyInitializedBean;
}
protected BeanWrapperImpl createBeanWrapper(BeanDefinition merged, Object bean) {
BeanMetadata metadata;
if (merged.isSingleton()) {
metadata = new BeanMetadata(bean.getClass());
}
else {
// fast access from cache
metadata = BeanMetadata.forInstance(bean);
}
BeanWrapperImpl beanWrapper = new BeanWrapperImpl(bean, metadata);
initBeanWrapper(beanWrapper);
return beanWrapper;
}
private boolean isEarlySingletonExposure(BeanDefinition definition, String beanName) {
return definition.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName);
}
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
*
* @param beanName the name of the bean (for error handling purposes)
* @param merged the merged bean merged for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition merged, Object bean) {
Object exposedObject = bean;
if (!merged.isSynthetic()) {
for (SmartInstantiationAwareBeanPostProcessor bp : postProcessors().smartInstantiation) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
@Override
public Object initializeBean(Object existingBean) throws BeansException {
return initializeBean(existingBean, BeanDefinitionBuilder.defaultBeanName(existingBean.getClass()));
}
@Override
public Object initializeBean(Object existingBean, String beanName) {
return initializeBean(existingBean, beanName, null);
}
/**
* Fully initialize the given raw bean, applying factory callbacks such as
* {@code setBeanName} and {@code setBeanFactory}, also applying all bean post
* processors (including ones which might wrap the given raw bean).
*
* Note that no bean merged of the given name has to exist in the bean
* factory. The passed-in bean name will simply be used for callbacks but not
* checked against the registered bean definitions.
*
* @param bean the new bean instance we may need to initialize
* @param def the bean def of the bean
* @return the bean instance to use, either the original or a wrapped one
* @throws BeanInitializationException if the initialization failed
* @see BeanNameAware
* @see BeanClassLoaderAware
* @see BeanFactoryAware
* @see #applyBeanPostProcessorsBeforeInitialization
* @see #invokeInitMethods
* @see #applyBeanPostProcessorsAfterInitialization
*/
public Object initializeBean(Object bean, String beanName, @Nullable RootBeanDefinition def) throws BeansException {
invokeAwareMethods(bean, beanName);
if (def == null || !def.isSynthetic()) {
bean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
invokeInitMethods(beanName, bean, def);
}
catch (Throwable ex) {
throw new BeanCreationException(
def != null ? def.getResourceDescription() : null, beanName, ex.getMessage(), ex);
}
if (def == null || !def.isSynthetic()) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
return bean;
}
private void invokeAwareMethods(Object bean, String beanName) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}
}
}
/**
* Give a bean a chance to initialize itself after all its properties are set,
* and a chance to know about its owning bean factory (this object).
*
This means checking whether the bean implements {@link InitializingBean}
* or defines any custom init methods, and invoking the necessary callback(s)
* if it does.
*
* @param beanName the bean name in the factory (for debugging purposes)
* @param bean the new bean instance we may need to initialize
* @param def the merged bean definition that the bean was created with
* (can also be {@code null}, if given an existing bean instance)
* @throws Throwable if thrown by init methods or by the invocation process
* @see infra.stereotype.Component
* @see InitializingBean
* @see jakarta.annotation.PostConstruct
*/
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition def) throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (def == null || !def.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
if (log.isTraceEnabled()) {
log.trace("Invoking afterPropertiesSet() on bean with name '{}'", beanName);
}
((InitializingBean) bean).afterPropertiesSet();
}
if (def != null) {
Method[] methods = initMethodArray(beanName, isInitializingBean, bean, def);
if (ObjectUtils.isNotEmpty(methods)) {
DependencyInjector injector = getInjector();
// invoke or initMethods defined in @Component
for (Method method : methods) {
if (log.isTraceEnabled()) {
log.trace("Invoking init method '{}' on bean with name '{}'", method.getName(), beanName);
}
ReflectionUtils.makeAccessible(method);
injector.inject(method, bean);
}
}
}
}
private Method[] initMethodArray(String beanName, boolean isInitializingBean, Object bean, RootBeanDefinition def) {
Method[] initMethodArray = def.initMethodArray;
if (def.initMethodArray == null) {
String[] initMethodNames = def.getInitMethodNames();
if (ObjectUtils.isNotEmpty(initMethodNames)) {
ArrayList methods = new ArrayList<>(2);
for (String initMethodName : initMethodNames) {
if (StringUtils.isNotEmpty(initMethodName)
&& !(isInitializingBean && "afterPropertiesSet".equals(initMethodName))
&& !def.hasAnyExternallyManagedInitMethod(initMethodName)) {
Class> beanClass = bean.getClass();
MethodDescriptor descriptor = MethodDescriptor.create(beanName, beanClass, initMethodName);
String methodName = descriptor.methodName();
Method initMethod = def.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(descriptor.declaringClass(), methodName) :
ReflectionUtils.getMethodIfAvailable(beanClass, methodName);
if (initMethod == null) {
if (def.isEnforceInitMethod()) {
throw new BeanDefinitionValidationException("Could not find an init method named '%s' on bean with name '%s'"
.formatted(initMethodName, beanName));
}
else {
if (log.isTraceEnabled()) {
log.trace("No default init method named '{}' found on bean with name '{}'",
initMethodName, beanName);
}
// Ignore non-existent default lifecycle methods.
continue;
}
}
Method methodToInvoke = ReflectionUtils.getPubliclyAccessibleMethodIfPossible(initMethod, beanClass);
methods.add(methodToInvoke);
}
}
AnnotationAwareOrderComparator.sort(methods);
initMethodArray = methods.toArray(BeanDefinition.EMPTY_METHOD);
}
else {
initMethodArray = BeanDefinition.EMPTY_METHOD;
}
def.initMethodArray = initMethodArray;
}
return initMethodArray;
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (InitializationBeanPostProcessor processor : postProcessors().initialization) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (InitializationBeanPostProcessor processor : postProcessors().initialization) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
/**
* Apply before-instantiation post-processors, resolving whether there is a
* before-instantiation shortcut for the specified bean.
*
* @param beanName the name of the bean
* @param definition the bean merged for the bean
* @return the shortcut-determined bean instance, or {@code null} if none
*/
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition definition) {
Object bean = null;
if (!Boolean.FALSE.equals(definition.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!definition.isSynthetic()) {
Class> targetType = determineTargetType(beanName, definition);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
definition.beforeInstantiationResolved = (bean != null);
}
return bean;
}
/**
* Apply InstantiationAwareBeanPostProcessors to the specified bean merged
* (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
* Any returned object will be used as the bean instead of actually instantiating
* the target bean. A {@code null} return value from the post-processor will
* result in the target bean being instantiated.
*
* @param beanClass the class of the bean to be instantiated
* @param beanName the name of the bean
* @return the bean object to use instead of a default instance of the target bean, or {@code null}
* @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
*/
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName) {
for (InstantiationAwareBeanPostProcessor processor : postProcessors().instantiation) {
Object result = processor.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
return null;
}
/**
* Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
* invoking their {@code postProcessMergedBeanDefinition} methods.
*
* @param mbd the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
* @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
*/
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class> beanType, String beanName) {
for (MergedBeanDefinitionPostProcessor processor : postProcessors().definitions) {
processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
/**
* Create a new instance for the specified bean, using an appropriate instantiation strategy:
* factory method, constructor autowiring, or simple instantiation.
*
* @param beanName the name of the bean
* @param merged the bean merged for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a BeanWrapper for the new instance
* @see #obtainFromSupplier
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition merged, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class> beanClass = resolveBeanClass(beanName, merged);
if (beanClass != null
&& !Modifier.isPublic(beanClass.getModifiers())
&& !merged.isNonPublicAccessAllowed()) {
throw new BeanCreationException(merged.getResourceDescription(),
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
if (args == null) {
Supplier> instanceSupplier = merged.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(merged, instanceSupplier, beanName); // maybe null
}
}
if (merged.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, merged, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized(merged.constructorArgumentLock) {
if (merged.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = merged.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, merged, null, null);
}
else {
return instantiateBean(beanName, merged);
}
}
// Candidate constructors for autowiring?
Constructor>[] constructors = determineConstructorsFromPostProcessors(beanClass, beanName);
if (constructors != null
|| merged.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR
|| merged.hasConstructorArgumentValues()
|| ObjectUtils.isNotEmpty(args)) {
return autowireConstructor(beanName, merged, constructors, args);
}
// Preferred constructors for default construction?
constructors = merged.getPreferredConstructors();
if (ObjectUtils.isNotEmpty(constructors)) {
return autowireConstructor(beanName, merged, constructors, args);
}
if (beanClass != null && !merged.hasConstructorArgumentValues()) {
Constructor> selected = BeanUtils.getConstructor(beanClass);
if (selected != null && selected.getParameterCount() > 0) {
try {
return autowireConstructor(beanName, merged, new Constructor[] { selected }, args);
}
catch (BeansException ignored) { }
}
// fallback to default constructor
}
return instantiateBean(beanName, merged);
}
/**
* Obtain a bean instance from the given supplier.
*
* @param merged merged bean def
* @param instanceSupplier the configured supplier
* @param beanName the corresponding bean name
* @return a BeanWrapper for the new instance
* @since 4.0
*/
protected BeanWrapper obtainFromSupplier(RootBeanDefinition merged, Supplier> instanceSupplier, String beanName) {
Object instance = obtainInstanceFromSupplier(instanceSupplier, beanName, merged);
if (instance == null) {
instance = NullValue.INSTANCE;
}
return createBeanWrapper(merged, instance);
}
/**
* Obtain a bean instance from the given supplier.
*
* @param supplier the configured supplier
* @param beanName the corresponding bean name
* @param merged the bean definition for the bean
* @return the bean instance (possibly {@code null})
*/
private Object obtainInstanceFromSupplier(Supplier> supplier, String beanName, RootBeanDefinition merged) {
String outerBean = currentlyCreatedBean.get();
currentlyCreatedBean.set(beanName);
try {
if (supplier instanceof InstanceSupplier> instanceSupplier) {
return instanceSupplier.get(RegisteredBean.of(this, beanName, merged));
}
if (supplier instanceof ThrowingSupplier> throwableSupplier) {
return throwableSupplier.getWithException();
}
return supplier.get();
}
catch (Throwable ex) {
if (ex instanceof BeansException beansException) {
throw beansException;
}
throw new BeanCreationException(beanName, "Instantiation of supplied bean failed", ex);
}
finally {
if (outerBean != null) {
currentlyCreatedBean.set(outerBean);
}
else {
currentlyCreatedBean.remove();
}
}
}
/**
* Overridden in order to implicitly register the currently created bean as
* dependent on further beans getting programmatically retrieved during a
* {@link Supplier} callback.
*
* @see #obtainFromSupplier
* @since 4.0
*/
@Nullable
@Override
protected Object handleFactoryBean(String name, String beanName,
@Nullable RootBeanDefinition definition, Object beanInstance) throws BeansException {
String currentlyCreatedBean = this.currentlyCreatedBean.get();
if (currentlyCreatedBean != null) {
registerDependentBean(beanName, currentlyCreatedBean);
}
return super.handleFactoryBean(name, beanName, definition, beanInstance);
}
/**
* Instantiate the bean using a named factory method. The method may be static, if the
* merged parameter specifies a class, rather than a factoryBean, or an instance variable
* on a factory object itself configured using Dependency Injection.
*
* @param beanName beanName the name of the bean
* @param mbd the bean merged for the bean
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (implying the use of constructor argument values from bean merged)
* @return a BeanWrapper for the new instance
* @see #getBean(String, Object[])
* @since 4.0
*/
protected BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
*
This corresponds to constructor injection: In this mode, a Framework
* bean factory is able to host components that expect constructor-based
* dependency resolution.
*
* @param beanName the name of the bean
* @param mbd the bean merged for the bean
* @param ctors the chosen candidate constructors
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (implying the use of constructor argument values from bean merged)
* @return a BeanWrapper for the new instance
* @since 4.0
*/
protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
/**
* Determine candidate constructors to use for the given bean, checking all registered
* {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
*
* @param beanClass the raw class of the bean
* @param beanName the name of the bean
* @return the candidate constructors, or {@code null} if none specified
* @throws BeansException in case of errors
* @see SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
*/
@Nullable
protected Constructor>[] determineConstructorsFromPostProcessors(
@Nullable Class> beanClass, String beanName) throws BeansException {
if (beanClass != null) {
var smartInstantiation = postProcessors().smartInstantiation;
if (!smartInstantiation.isEmpty()) {
for (SmartInstantiationAwareBeanPostProcessor bp : smartInstantiation) {
var ctors = bp.determineCandidateConstructors(beanClass, beanName);
if (ctors != null) {
return ctors;
}
}
}
}
return null;
}
/**
* Instantiate the given bean using its default constructor.
*
* @param beanName the name of the bean
* @param merged the bean merged for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition merged) {
try {
Object beanInstance = getInstantiationStrategy().instantiate(merged, beanName, this);
return createBeanWrapper(merged, beanInstance);
}
catch (Throwable ex) {
throw new BeanCreationException(merged.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
@Override
public Object autowire(Class> beanClass) throws BeansException {
return autowire(beanClass, AUTOWIRE_CONSTRUCTOR, false);
}
@Override
public Object autowire(Class> beanClass, int autowireMode) throws BeansException {
return autowire(beanClass, autowireMode, false);
}
@Override
public Object autowire(Class> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
// Use non-singleton bean merged, to avoid registering bean as dependent bean.
RootBeanDefinition merged = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
merged.setScope(BeanDefinition.SCOPE_PROTOTYPE);
if (merged.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) {
return autowireConstructor(beanClass.getName(), merged, null, null).getWrappedInstance();
}
else {
Object bean = getInstantiationStrategy().instantiate(merged, null, this);
BeanWrapperImpl beanWrapper = createBeanWrapper(merged, bean);
populateBean(beanClass.getName(), merged, beanWrapper);
return bean;
}
}
@Override
public void autowireBeanProperties(Object existingBean, int autowireMode) throws BeansException {
autowireBeanProperties(existingBean, autowireMode, false);
}
@Override
public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException {
if (autowireMode == AUTOWIRE_CONSTRUCTOR) {
throw new IllegalArgumentException("AUTOWIRE_CONSTRUCTOR not supported for existing bean instance");
}
// Use non-singleton bean merged, to avoid registering bean as dependent bean.
RootBeanDefinition merged =
new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck);
merged.setScope(BeanDefinition.SCOPE_PROTOTYPE);
BeanWrapper bw = createBeanWrapper(merged, existingBean);
populateBean(merged.getBeanClass().getName(), merged, bw);
}
@Override
public void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException {
markBeanAsCreated(beanName);
BeanDefinition merged = getMergedBeanDefinition(beanName);
BeanWrapper bw = createBeanWrapper(merged, existingBean);
applyPropertyValues(beanName, merged, bw, merged.getPropertyValues());
}
@Override
public Object configureBean(Object existingBean, String beanName) throws BeansException {
markBeanAsCreated(beanName);
BeanDefinition merged = getMergedBeanDefinition(beanName);
RootBeanDefinition bd = null;
if (merged instanceof RootBeanDefinition rbd) {
bd = rbd.isPrototype() ? rbd : rbd.cloneBeanDefinition();
}
if (bd == null) {
bd = new RootBeanDefinition(merged);
}
if (!bd.isPrototype()) {
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bd.allowCaching = ClassUtils.isCacheSafe(ClassUtils.getUserClass(existingBean), getBeanClassLoader());
}
BeanWrapper bw = createBeanWrapper(merged, existingBean);
populateBean(beanName, bd, bw);
return initializeBean(existingBean, beanName, bd);
}
public void populateBean(String beanName, RootBeanDefinition merged, BeanWrapper beanWrapper) {
if (beanWrapper == null) {
if (merged.hasPropertyValues()) {
throw new BeanCreationException(
merged.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
if (beanWrapper.getWrappedClass().isRecord()) {
if (merged.hasPropertyValues()) {
throw new BeanCreationException(
merged.getResourceDescription(), beanName, "Cannot apply property values to a record");
}
else {
// Skip property population phase for records since they are immutable.
return;
}
}
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
Object wrappedInstance = beanWrapper.getWrappedInstance();
if (!merged.isSynthetic()) {
for (InstantiationAwareBeanPostProcessor processor : postProcessors().instantiation) {
if (!processor.postProcessAfterInstantiation(wrappedInstance, beanName)) {
return;
}
}
}
// maybe null
PropertyValues propertyValues = merged.getPropertyValues();
int resolvedAutowireMode = merged.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
PropertyValues newPvs = new PropertyValues(propertyValues);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, merged, beanWrapper, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, merged, beanWrapper, newPvs);
}
propertyValues = newPvs;
}
boolean hasDependenciesProcessors = !postProcessors().dependencies.isEmpty();
if (hasDependenciesProcessors && !merged.isSynthetic() && merged.isEnableDependencyInjection()) {
// -----------------------------------------------
// apply dependency injection (DI)
// apply outside framework expanded
// -----------------------------------------------
if (propertyValues == null) {
propertyValues = merged.getPropertyValues();
}
for (DependenciesBeanPostProcessor processor : postProcessors().dependencies) {
PropertyValues pvsToUse = processor.processDependencies(propertyValues, wrappedInstance, beanName);
if (pvsToUse == null) {
return;
}
propertyValues = pvsToUse;
}
}
boolean needsDepCheck = merged.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;
if (needsDepCheck) {
BeanProperty[] filteredPds = filterBeanPropertiesForDependencyCheck(beanWrapper, merged.allowCaching);
checkDependencies(beanName, merged, filteredPds, propertyValues);
}
if (propertyValues != null) {
applyPropertyValues(beanName, merged, beanWrapper, propertyValues);
}
}
/**
* Apply the given property values, resolving any runtime references
* to other beans in this bean factory. Must use deep copy, so we
* don't permanently modify this property.
*
* @param beanName the bean name passed for better exception information
* @param definition the bean merged
* @param beanWrapper the BeanWrapper wrapping the target object
* @param pvs the new property values
*/
protected void applyPropertyValues(String beanName,
BeanDefinition definition, BeanWrapper beanWrapper, @Nullable PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
}
if (pvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
beanWrapper.setPropertyValues(pvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
definition.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
List original = pvs.asList();
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = beanWrapper;
}
var valueResolver = new BeanDefinitionValueResolver(this, beanName, definition, converter);
// Create a deep copy, resolving any references for values.
ArrayList deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
Object resolvedValue;
if (originalValue instanceof PropertyValueRetriever retriever) {
resolvedValue = retriever.retrieve(propertyName, beanWrapper, this);
if (resolvedValue == PropertyValueRetriever.DO_NOT_SET) {
continue;
}
}
else {
if (originalValue == AutowiredPropertyMarker.INSTANCE) {
if (!beanWrapper.isWritableProperty(propertyName)) {
throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
}
BeanProperty property = beanWrapper.getBeanProperty(propertyName);
MethodParameter writeMethodParameter = property.getWriteMethodParameter();
if (writeMethodParameter != null) {
originalValue = new DependencyDescriptor(writeMethodParameter, true);
}
else {
originalValue = new DependencyDescriptor(property.getField(), true);
}
}
resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
}
Object convertedValue = resolvedValue;
boolean convertible = beanWrapper.isWritableProperty(propertyName)
&& !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, beanWrapper, converter);
}
// Possibly store converted value in merged bean merged,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue
&& !((TypedStringValue) originalValue).isDynamic()
&& !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (!resolveNecessary) {
pvs.setConverted();
}
// Set our (possibly massaged) deep copy.
try {
beanWrapper.setPropertyValues(new PropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
definition.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}
/**
* Convert the given value for the specified target property.
*/
@Nullable
private Object convertForProperty(@Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
if (converter instanceof BeanWrapperImpl) {
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else {
BeanProperty pd = bw.getBeanProperty(propertyName);
MethodParameter methodParam = pd.getWriteMethodParameter();
return converter.convertIfNecessary(value, pd.getType(), methodParam);
}
}
/**
* Fill in any missing property values with references to
* other beans in this factory if autowire is set to "byName".
*
* @param beanName the name of the bean we're wiring up.
* Useful for debugging messages; not used functionally.
* @param definition bean merged to update through autowiring
* @param bw the BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByName(String beanName, AbstractBeanDefinition definition, BeanWrapper bw, PropertyValues pvs) {
String[] propertyNames = unsatisfiedNonSimpleProperties(definition, bw);
for (String propertyName : propertyNames) {
if (containsBean(propertyName)) {
Object bean = getBean(propertyName);
pvs.add(propertyName, bean);
registerDependentBean(propertyName, beanName);
if (log.isTraceEnabled()) {
log.trace("Added autowiring by name from bean name '{}' via property '{}' to bean named '{}'",
beanName, propertyName, propertyName);
}
}
else {
if (log.isTraceEnabled()) {
log.trace("Not autowiring property '{}' of bean '{]' by name: no matching bean found",
propertyName, beanName);
}
}
}
}
/**
* Abstract method defining "autowire by type" (bean properties by type) behavior.
* This is like PicoContainer default, in which there must be exactly one bean
* of the property type in the bean factory. This makes bean factories simple to
* configure for small namespaces, but doesn't work as well as standard Framework
* behavior for bigger applications.
*
* @param beanName the name of the bean to autowire by type
* @param definition the merged bean merged to update through autowiring
* @param wrapper the BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByType(String beanName, BeanDefinition definition, BeanWrapper wrapper, PropertyValues pvs) {
BeanMetadata metadata = wrapper.getMetadata();
String[] propertyNames = unsatisfiedNonSimpleProperties(definition, wrapper);
LinkedHashSet autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
for (String propertyName : propertyNames) {
try {
BeanProperty beanProperty = metadata.obtainBeanProperty(propertyName);
// Don't try autowiring by type for type Object: never makes sense,
// even if it technically is an unsatisfied, non-simple property, non-writeable.
if (Object.class != beanProperty.getType() && beanProperty.isWriteable()) {
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !(wrapper.getWrappedInstance() instanceof PriorityOrdered);
DependencyDescriptor desc;
MethodParameter methodParam = beanProperty.getWriteMethodParameter();
if (methodParam != null) {
desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
}
else {
desc = new AutowireByTypeDependencyDescriptor(beanProperty.getField(), eager);
}
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, null);
if (autowiredArgument != null) {
pvs.add(propertyName, autowiredArgument);
}
for (String autowiredBeanName : autowiredBeanNames) {
registerDependentBean(autowiredBeanName, beanName);
if (log.isTraceEnabled()) {
log.trace("Autowiring by type from bean name '{}' via property '{}' to bean named '{}'",
beanName, propertyName, autowiredBeanName);
}
}
autowiredBeanNames.clear();
}
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(definition.getResourceDescription(), beanName, propertyName, ex);
}
}
}
/**
* Return an array of non-simple bean properties that are unsatisfied.
* These are probably unsatisfied references to other beans in the
* factory. Does not include simple properties like primitives or Strings.
*
* @param definition the merged bean merged the bean was created with
* @param wrapper the BeanWrapper the bean was created with
* @return an array of bean property names
* @see BeanUtils#isSimpleProperty
*/
protected String[] unsatisfiedNonSimpleProperties(BeanDefinition definition, BeanWrapper wrapper) {
TreeSet result = new TreeSet<>();
PropertyValues pvs = definition.getPropertyValues();
for (BeanProperty property : wrapper.getBeanProperties()) {
if (property.isWriteable()
&& !isExcludedFromDependencyCheck(property)
&& (pvs == null || !pvs.contains(property.getName()))
&& !BeanUtils.isSimpleProperty(property.getType())) {
result.add(property.getName());
}
}
return StringUtils.toStringArray(result);
}
/**
* Extract a filtered set of PropertyDescriptors from the given BeanWrapper,
* excluding ignored dependency types or properties defined on ignored dependency interfaces.
*
* @param bw the BeanWrapper the bean was created with
* @param cache whether to cache filtered PropertyDescriptors for the given bean Class
* @return the filtered PropertyDescriptors
* @see #isExcludedFromDependencyCheck
* @see #filterBeanPropertiesForDependencyCheck(BeanWrapper)
* @since 4.0
*/
protected BeanProperty[] filterBeanPropertiesForDependencyCheck(BeanWrapper bw, boolean cache) {
BeanProperty[] filtered = filteredBeanPropertiesCache.get(bw.getWrappedClass());
if (filtered == null) {
filtered = filterBeanPropertiesForDependencyCheck(bw);
if (cache) {
BeanProperty[] existing = filteredBeanPropertiesCache.putIfAbsent(bw.getWrappedClass(), filtered);
if (existing != null) {
filtered = existing;
}
}
}
return filtered;
}
/**
* Extract a filtered set of BeanProperties from the given BeanWrapper,
* excluding ignored dependency types or properties defined on ignored dependency interfaces.
*
* @param bw the BeanWrapper the bean was created with
* @return the filtered PropertyDescriptors
* @see #isExcludedFromDependencyCheck
*/
protected BeanProperty[] filterBeanPropertiesForDependencyCheck(BeanWrapper bw) {
ArrayList pds = new ArrayList<>(bw.getBeanProperties());
pds.removeIf(this::isExcludedFromDependencyCheck);
return pds.toArray(new BeanProperty[0]);
}
/**
* Perform a dependency check that all properties exposed have been set,
* if desired. Dependency checks can be objects (collaborating beans),
* simple (primitives and String), or all (both).
*
* @param beanName the name of the bean
* @param mbd the merged bean merged the bean was created with
* @param beanProperties the relevant property descriptors for the target bean
* @param propertyValues the property values to be applied to the bean
* @see #isExcludedFromDependencyCheck(BeanProperty)
*/
protected void checkDependencies(String beanName, AbstractBeanDefinition mbd,
BeanProperty[] beanProperties, @Nullable PropertyValues propertyValues) throws UnsatisfiedDependencyException {
int dependencyCheck = mbd.getDependencyCheck();
for (BeanProperty property : beanProperties) {
if (property.isWriteable() && (propertyValues == null || !propertyValues.contains(property.getName()))) {
boolean isSimple = BeanUtils.isSimpleProperty(property.getType());
boolean unsatisfied = (dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_ALL)
|| (isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE)
|| (!isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
if (unsatisfied) {
throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, property.getName(),
"Set this property value or disable dependency checking for this bean.");
}
}
}
}
/**
* Determine whether the given bean property is excluded from dependency checks.
* This implementation excludes properties defined by CGLIB and
* properties whose type matches an ignored dependency type or which
* are defined by an ignored dependency interface.
*
* @param property the BeanProperty of the bean property
* @return whether the bean property is excluded
* @see #ignoreDependencyType(Class)
* @see #ignoreDependencyInterface(Class)
*/
protected boolean isExcludedFromDependencyCheck(BeanProperty property) {
return ignoredDependencyTypes.contains(property.getType())
|| AutowireUtils.isExcludedFromDependencyCheck(property)
|| AutowireUtils.isSetterDefinedInInterface(property, ignoredDependencyInterfaces);
}
@Override
public void destroyBean(Object existingBean) {
new DisposableBeanAdapter(existingBean, postProcessors().destruction).destroy();
}
//---------------------------------------------------------------------
// Implementation of AbstractBeanFactory class
//---------------------------------------------------------------------
/**
* Applies the {@code postProcessAfterInitialization} callback of all
* registered BeanPostProcessors, giving them a chance to post-process the
* object obtained from FactoryBeans (for example, to auto-proxy them).
*
* @see #applyBeanPostProcessorsAfterInitialization
*/
@Override
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
return applyBeanPostProcessorsAfterInitialization(object, beanName);
}
/**
* Overridden to clear FactoryBean instance cache as well.
*/
@Override
public void removeSingleton(String beanName) {
super.removeSingleton(beanName);
this.factoryBeanInstanceCache.remove(beanName);
}
/**
* Overridden to clear FactoryBean instance cache as well.
*/
@Override
protected void clearSingletonCache() {
super.clearSingletonCache();
this.factoryBeanInstanceCache.clear();
}
@Override
@Nullable
protected Class> predictBeanType(String beanName, RootBeanDefinition merged, Class>... typesToMatch) {
Class> targetType = determineTargetType(beanName, merged, typesToMatch);
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
// eventual type after a before-instantiation shortcut.
if (targetType != null && !merged.isSynthetic()) {
ArrayList instantiation = postProcessors().smartInstantiation;
if (!instantiation.isEmpty()) {
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
for (SmartInstantiationAwareBeanPostProcessor bp : instantiation) {
Class> predicted = bp.predictBeanType(targetType, beanName);
if (predicted != null &&
(!matchingOnlyFactoryBean || FactoryBean.class.isAssignableFrom(predicted))) {
return predicted;
}
}
}
}
return targetType;
}
/**
* Determine the target type for the given bean merged.
*
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean merged for the bean
* @param typesToMatch the types to match in case of internal type matching purposes
* (also signals that the returned {@code Class} will never be exposed to application code)
* @return the type for the bean if determinable, or {@code null} otherwise
*/
@Nullable
protected Class> determineTargetType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
Class> targetType = mbd.getTargetType();
if (targetType == null) {
if (mbd.getFactoryMethodName() != null) {
targetType = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
}
else {
targetType = resolveBeanClass(beanName, mbd, typesToMatch);
if (mbd.hasBeanClass()) {
targetType = getInstantiationStrategy().getActualBeanClass(mbd, beanName, this);
}
}
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
mbd.resolvedTargetType = targetType;
}
}
return targetType;
}
/**
* Determine the target type for the given bean merged which is based on
* a factory method. Only called if there is no singleton instance registered
* for the target bean already.
* This implementation determines the type matching {@link #createBean}'s
* different creation strategies. As far as possible, we'll perform static
* type checking to avoid creation of the target bean.
*
* @param beanName the name of the bean (for error handling purposes)
* @param merged the merged bean merged for the bean
* @return the type for the bean if determinable, or {@code null} otherwise
* @see #createBean
*/
@Nullable
protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition merged, Class>... typesToMatch) {
ResolvableType cachedReturnType = merged.factoryMethodReturnType;
if (cachedReturnType != null) {
return cachedReturnType.resolve();
}
Class> commonType = null;
Method uniqueCandidate = merged.factoryMethodToIntrospect;
if (uniqueCandidate == null) {
Class> factoryClass;
boolean isStatic = true;
String factoryBeanName = merged.getFactoryBeanName();
if (factoryBeanName != null) {
if (factoryBeanName.equals(beanName)) {
throw new BeanDefinitionStoreException(merged.getResourceDescription(),
"factory-bean reference points back to the same bean merged");
}
// Check declared factory method return type on factory class.
factoryClass = getType(factoryBeanName);
isStatic = false;
}
else {
// Check declared factory method return type on bean class.
factoryClass = resolveBeanClass(beanName, merged, typesToMatch);
}
if (factoryClass == null) {
return null;
}
factoryClass = ClassUtils.getUserClass(factoryClass);
// If all factory methods have the same return type, return that type.
// Can't clearly figure out exact method due to type converting / autowiring!
int minNrOfArgs = merged.hasConstructorArgumentValues()
? merged.getConstructorArgumentValues().getArgumentCount() : 0;
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(
factoryClass, ReflectionUtils.USER_DECLARED_METHODS);
for (Method candidate : candidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic
&& candidate.getParameterCount() >= minNrOfArgs
&& merged.isFactoryMethod(candidate)) {
// Declared type variables to inspect?
if (candidate.getTypeParameters().length > 0) {
try {
// Fully resolve parameter names and argument values.
ConstructorArgumentValues cav = merged.getConstructorArgumentValues();
Class>[] paramTypes = candidate.getParameterTypes();
String[] paramNames = null;
if (cav.containsNamedArgument()) {
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
}
var usedValueHolders = new HashSet(paramTypes.length);
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < args.length; i++) {
String requiredName = paramNames != null ? paramNames[i] : null;
ConstructorArgumentValues.ValueHolder valueHolder =
cav.getArgumentValue(i, paramTypes[i], requiredName, usedValueHolders);
if (valueHolder == null) {
valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
}
if (valueHolder != null) {
args[i] = valueHolder.getValue();
usedValueHolders.add(valueHolder);
}
}
Class> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
candidate, args, getBeanClassLoader());
uniqueCandidate = commonType == null && returnType == candidate.getReturnType()
? candidate : null;
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
if (commonType == null) {
// Ambiguous return types found: return null to indicate "not determinable".
return null;
}
}
catch (Throwable ex) {
if (log.isDebugEnabled()) {
log.debug("Failed to resolve generic return type for factory method: {}", ex.toString());
}
}
}
else {
uniqueCandidate = commonType == null ? candidate : null;
commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
if (commonType == null) {
// Ambiguous return types found: return null to indicate "not determinable".
return null;
}
}
}
}
merged.factoryMethodToIntrospect = uniqueCandidate;
if (commonType == null) {
return null;
}
}
// Common return type found: all factory methods return same type. For a non-parameterized
// unique candidate, cache the full type declaration context of the target factory method.
try {
cachedReturnType = uniqueCandidate != null
? ResolvableType.forReturnType(uniqueCandidate)
: ResolvableType.forClass(commonType);
merged.factoryMethodReturnType = cachedReturnType;
return cachedReturnType.resolve();
}
catch (LinkageError err) {
// E.g. a NoClassDefFoundError for a generic method return type
if (log.isDebugEnabled()) {
log.debug("Failed to resolve type for factory method of bean '{}': {}",
beanName, uniqueCandidate != null ? uniqueCandidate : commonType, err);
}
return null;
}
}
/**
* This implementation attempts to query the FactoryBean's generic parameter metadata
* if present to determine the object type. If not present, i.e. the FactoryBean is
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
* on a plain instance of the FactoryBean, without bean properties applied yet.
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
* full creation of the FactoryBean is used as fallback (through delegation to the
* superclass's implementation).
* The shortcut check for a FactoryBean is only applied in case of a singleton
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
* it will be fully created to check the type of its exposed object.
*/
@Override
protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition merged, boolean allowInit) {
ResolvableType result;
// Check if the bean definition itself has defined the type with an attribute
try {
result = getTypeForFactoryBeanFromAttributes(merged);
if (result != ResolvableType.NONE) {
return result;
}
}
catch (IllegalArgumentException ex) {
throw new BeanDefinitionStoreException(merged.getResourceDescription(), beanName,
String.valueOf(ex.getMessage()));
}
// For instance supplied beans, try the target type and bean class immediately
if (merged.getInstanceSupplier() != null) {
result = getFactoryBeanGeneric(merged);
if (result != null) {
return result;
}
}
// Consider factory methods
String factoryBeanName = merged.getFactoryBeanName();
String factoryMethodName = merged.getFactoryMethodName();
// Scan the factory bean methods
if (factoryBeanName != null) {
if (factoryMethodName != null) {
// Try to obtain the FactoryBean's object type from its factory method
// declaration without instantiating the containing bean at all.
Class> factoryBeanClass;
BeanDefinition factoryBeanDefinition = getBeanDefinition(factoryBeanName);
if (factoryBeanDefinition instanceof AbstractBeanDefinition abd && abd.hasBeanClass()) {
factoryBeanClass = abd.getBeanClass();
}
else {
RootBeanDefinition factoryMerged = getMergedBeanDefinition(factoryBeanName, factoryBeanDefinition);
factoryBeanClass = determineTargetType(factoryBeanName, factoryMerged);
}
if (factoryBeanClass != null) {
result = getTypeForFactoryBeanFromMethod(factoryBeanClass, factoryMethodName);
if (result.isResolved()) {
return result;
}
}
}
// If not resolvable above and the referenced factory bean doesn't exist yet,
// exit here - we don't want to force the creation of another bean just to
// obtain a FactoryBean's object type...
if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
return ResolvableType.NONE;
}
}
// If we're allowed, we can create the factory bean and call getObjectType() early
if (allowInit) {
FactoryBean> factoryBean = getFactoryBeanForTypeCheck(beanName, merged);
if (factoryBean != null) {
// Try to obtain the FactoryBean's object type from this early stage of the instance.
Class> type = getTypeForFactoryBean(factoryBean);
if (type != null) {
return ResolvableType.forClass(type);
}
// No type found for shortcut FactoryBean instance:
// fall back to full creation of the FactoryBean instance.
return super.getTypeForFactoryBean(beanName, merged, true);
}
}
if (factoryBeanName == null && merged.hasBeanClass() && factoryMethodName != null) {
// No early bean instantiation possible: determine FactoryBean's type from
// static factory method signature or from class inheritance hierarchy...
return getTypeForFactoryBeanFromMethod(merged.getBeanClass(), factoryMethodName);
}
// For regular beans, try the target type and bean class as fallback
if (merged.getInstanceSupplier() == null) {
result = getFactoryBeanGeneric(merged);
if (result != null) {
return result;
}
}
// FactoryBean type not resolvable
return ResolvableType.NONE;
}
@Nullable
private ResolvableType getFactoryBeanGeneric(RootBeanDefinition merged) {
ResolvableType result = getFactoryBeanGeneric(merged.targetType);
if (result.isResolved()) {
return result;
}
if (merged.hasBeanClass()) {
result = getFactoryBeanGeneric(ResolvableType.forClass(merged.getBeanClass()));
if (result.isResolved()) {
return result;
}
}
return null;
}
private ResolvableType getFactoryBeanGeneric(@Nullable ResolvableType type) {
if (type == null) {
return ResolvableType.NONE;
}
return type.as(FactoryBean.class).getGeneric();
}
/**
* Introspect the factory method signatures on the given bean class,
* trying to find a common {@code FactoryBean} object type declared there.
*
* @param beanClass the bean class to find the factory method on
* @param factoryMethodName the name of the factory method
* @return the common {@code FactoryBean} object type, or {@code null} if none
*/
private ResolvableType getTypeForFactoryBeanFromMethod(Class> beanClass, String factoryMethodName) {
// CGLIB subclass methods hide generic parameters; look at the original user class.
Class> factoryBeanClass = ClassUtils.getUserClass(beanClass);
FactoryBeanMethodTypeFinder finder = new FactoryBeanMethodTypeFinder(factoryMethodName);
ReflectionUtils.doWithMethods(factoryBeanClass, finder, ReflectionUtils.USER_DECLARED_METHODS);
return finder.getResult();
}
@Nullable
private FactoryBean> getFactoryBeanForTypeCheck(String beanName, RootBeanDefinition def) {
if (def.isSingleton()) {
BeanWrapper beanWrapper = this.factoryBeanInstanceCache.get(beanName);
if (beanWrapper != null && beanWrapper.getWrappedInstance() instanceof FactoryBean> factory) {
return factory;
}
Object instance = getSingleton(beanName, false);
if (instance == NullValue.INSTANCE) { // created and its instance is null
return null;
}
if (instance instanceof FactoryBean> factory) {
return factory;
}
if (isSingletonCurrentlyInCreation(beanName)
|| (def.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(def.getFactoryBeanName()))) {
return null;
}
try {
// Mark this bean as currently in creation, even if just partially.
beforeSingletonCreation(beanName);
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
instance = resolveBeforeInstantiation(beanName, def);
if (instance == null) {
beanWrapper = createBeanInstance(beanName, def, null);
instance = beanWrapper.getWrappedInstance();
}
}
catch (UnsatisfiedDependencyException ex) {
// Don't swallow, probably misconfiguration...
throw ex;
}
catch (BeanCreationException ex) {
// Don't swallow a linkage error since it contains a full stacktrace on
// first occurrence... and just a plain NoClassDefFoundError afterwards.
if (ex.contains(LinkageError.class)) {
throw ex;
}
// Instantiation failure, maybe too early...
if (log.isDebugEnabled()) {
log.debug("Bean creation exception on singleton FactoryBean type check: {}", ex.toString());
}
onSuppressedException(ex);
return null;
}
finally {
// Finished partial creation of this bean.
afterSingletonCreation(beanName);
}
// put to factoryBeanInstanceCache
FactoryBean> factory = getFactoryBean(beanName, instance);
if (beanWrapper != null) {
factoryBeanInstanceCache.put(beanName, beanWrapper);
}
return factory;
}
else {
// prototype
if (isPrototypeCurrentlyInCreation(beanName)) {
return null;
}
// Prototype
Object instance;
try {
// Mark this bean as currently in creation, even if just partially.
beforePrototypeCreation(beanName);
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
instance = resolveBeforeInstantiation(beanName, def);
if (instance == null) {
BeanWrapper beanWrapper = createBeanInstance(beanName, def, null);
instance = beanWrapper.getWrappedInstance();
}
}
catch (UnsatisfiedDependencyException ex) {
// Don't swallow, probably misconfiguration...
throw ex;
}
catch (BeanCreationException ex) {
// Instantiation failure, maybe too early...
if (log.isDebugEnabled()) {
log.debug("Bean creation exception on non-singleton FactoryBean type check: {}", ex.toString());
}
onSuppressedException(ex);
return null;
}
finally {
// Finished partial creation of this bean.
afterPrototypeCreation(beanName);
}
return getFactoryBean(beanName, instance);
}
}
@Override
public Object resolveBeanByName(String name, DependencyDescriptor descriptor) {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
return getBean(name, descriptor.getDependencyType());
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
/**
* Set the instantiation strategy to use for creating bean instances.
* Default is CglibSubclassingInstantiationStrategy.
*
* @since 4.0
*/
public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}
/**
* Return the instantiation strategy to use for creating bean instances.
*
* @since 4.0
*/
public InstantiationStrategy getInstantiationStrategy() {
return this.instantiationStrategy;
}
/**
* Set the ParameterNameDiscoverer to use for resolving method parameter
* names if needed (e.g. for constructor names).
*
Default is a {@link DefaultParameterNameDiscoverer}.
*
* @since 4.0
*/
public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* Return the ParameterNameDiscoverer to use for resolving method parameter
* names if needed.
*
* @since 4.0
*/
@Nullable
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
return this.parameterNameDiscoverer;
}
/**
* Set whether to allow circular references between beans - and automatically
* try to resolve them.
*
Note that circular reference resolution means that one of the involved beans
* will receive a reference to another bean that is not fully initialized yet.
* This can lead to subtle and not-so-subtle side effects on initialization;
* it does work fine for many scenarios, though.
*
Default is "true". Turn this off to throw an exception when encountering
* a circular reference, disallowing them completely.
*
NOTE: It is generally recommended to not rely on circular references
* between your beans. Refactor your application logic to have the two beans
* involved delegate to a third bean that encapsulates their common logic.
*
* @since 4.0
*/
public void setAllowCircularReferences(boolean allowCircularReferences) {
this.allowCircularReferences = allowCircularReferences;
}
/**
* Return whether to allow circular references between beans.
*
* @see #setAllowCircularReferences
* @since 4.0
*/
public boolean isAllowCircularReferences() {
return this.allowCircularReferences;
}
/**
* Set whether to allow the raw injection of a bean instance into some other
* bean's property, despite the injected bean eventually getting wrapped
* (for example, through AOP auto-proxying).
*
This will only be used as a last resort in case of a circular reference
* that cannot be resolved otherwise: essentially, preferring a raw instance
* getting injected over a failure of the entire bean wiring process.
*
Default is "false". Turn this on to allow for non-wrapped
* raw beans injected into some of your references.
*
NOTE: It is generally recommended to not rely on circular references
* between your beans, in particular with auto-proxying involved.
*
* @see #setAllowCircularReferences
* @since 4.0
*/
public void setAllowRawInjectionDespiteWrapping(boolean allowRawInjectionDespiteWrapping) {
this.allowRawInjectionDespiteWrapping = allowRawInjectionDespiteWrapping;
}
/**
* Return whether to allow the raw injection of a bean instance.
*
* @see #setAllowRawInjectionDespiteWrapping
* @since 4.0
*/
public boolean isAllowRawInjectionDespiteWrapping() {
return this.allowRawInjectionDespiteWrapping;
}
/**
* Ignore the given dependency type for autowiring:
* for example, String. Default is none.
*
* @since 4.0
*/
@Override
public void ignoreDependencyType(Class> type) {
this.ignoredDependencyTypes.add(type);
}
/**
* Ignore the given dependency interface for autowiring.
*
This will typically be used by application contexts to register
* dependencies that are resolved in other ways, like BeanFactory through
* BeanFactoryAware or ApplicationContext through ApplicationContextAware.
*
By default, only the BeanFactoryAware interface is ignored.
* For further types to ignore, invoke this method for each type.
*
* @see BeanFactoryAware
* @see infra.context.ApplicationContextAware
* @since 4.0
*/
@Override
public void ignoreDependencyInterface(Class> ifc) {
this.ignoredDependencyInterfaces.add(ifc);
}
@Override
public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
super.copyConfigurationFrom(otherFactory);
if (otherFactory instanceof AbstractAutowireCapableBeanFactory otherAutowireFactory) {
this.instantiationStrategy = otherAutowireFactory.instantiationStrategy;
this.allowCircularReferences = otherAutowireFactory.allowCircularReferences;
this.ignoredDependencyTypes.addAll(otherAutowireFactory.ignoredDependencyTypes);
this.ignoredDependencyInterfaces.addAll(otherAutowireFactory.ignoredDependencyInterfaces);
}
}
/**
* Expose the logger to collaborating delegates.
*/
Logger getLogger() {
return log;
}
/**
* Special DependencyDescriptor variant for Framework's good old autowire="byType" mode.
* Always optional; never considering the parameter name for choosing a primary candidate.
*/
private static class AutowireByTypeDependencyDescriptor extends DependencyDescriptor {
@Serial
private static final long serialVersionUID = 1L;
public AutowireByTypeDependencyDescriptor(MethodParameter methodParameter, boolean eager) {
super(methodParameter, false, eager);
}
public AutowireByTypeDependencyDescriptor(Field field, boolean eager) {
super(field, false, eager);
}
@Override
public String getDependencyName() {
return null;
}
}
/**
* {@link ReflectionUtils.MethodCallback} used to find {@link FactoryBean} type information.
*/
private static class FactoryBeanMethodTypeFinder implements ReflectionUtils.MethodCallback {
private final String factoryMethodName;
private ResolvableType result = ResolvableType.NONE;
FactoryBeanMethodTypeFinder(String factoryMethodName) {
this.factoryMethodName = factoryMethodName;
}
@Override
public void doWith(Method method) throws IllegalArgumentException {
if (isFactoryBeanMethod(method)) {
ResolvableType returnType = ResolvableType.forReturnType(method);
ResolvableType candidate = returnType.as(FactoryBean.class).getGeneric();
if (this.result == ResolvableType.NONE) {
this.result = candidate;
}
else {
Class> resolvedResult = this.result.resolve();
Class> commonAncestor = ClassUtils.determineCommonAncestor(candidate.resolve(), resolvedResult);
if (!ObjectUtils.nullSafeEquals(resolvedResult, commonAncestor)) {
this.result = ResolvableType.forClass(commonAncestor);
}
}
}
}
private boolean isFactoryBeanMethod(Method method) {
return (method.getName().equals(this.factoryMethodName) &&
FactoryBean.class.isAssignableFrom(method.getReturnType()));
}
ResolvableType getResult() {
Class> resolved = this.result.resolve();
boolean foundResult = resolved != null && resolved != Object.class;
return (foundResult ? this.result : ResolvableType.NONE);
}
}
}