
infra.beans.factory.support.BeanDefinitionValueResolver 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.lang.reflect.Array;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiFunction;
import infra.beans.BeanMetadataElement;
import infra.beans.BeanWrapper;
import infra.beans.BeanWrapperImpl;
import infra.beans.BeansException;
import infra.beans.TypeConverter;
import infra.beans.factory.BeanCreationException;
import infra.beans.factory.BeanDefinitionStoreException;
import infra.beans.factory.BeanFactory;
import infra.beans.factory.BeanFactoryUtils;
import infra.beans.factory.FactoryBean;
import infra.beans.factory.config.BeanDefinition;
import infra.beans.factory.config.BeanDefinitionHolder;
import infra.beans.factory.config.DependencyDescriptor;
import infra.beans.factory.config.NamedBeanHolder;
import infra.beans.factory.config.RuntimeBeanNameReference;
import infra.beans.factory.config.RuntimeBeanReference;
import infra.beans.factory.config.TypedStringValue;
import infra.lang.NullValue;
import infra.lang.Nullable;
import infra.util.ClassUtils;
import infra.util.CollectionUtils;
import infra.util.ObjectUtils;
import infra.util.StringUtils;
/**
* Helper class for use in bean factory implementations,
* resolving values contained in bean definition objects
* into the actual values applied to the target bean instance.
*
* Operates on an {@link AbstractBeanFactory} and a plain
* {@link BeanDefinition} object. Used by {@link AbstractAutowireCapableBeanFactory}.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @author Harry Yang
* @see AbstractAutowireCapableBeanFactory
* @since 4.0 2021/12/25 13:47
*/
public class BeanDefinitionValueResolver {
private final String beanName;
private final BeanDefinition beanDefinition;
private final AbstractAutowireCapableBeanFactory beanFactory;
private final TypeConverter typeConverter;
/**
* Create a BeanDefinitionValueResolver for the given BeanFactory and BeanDefinition,
* using the given {@link TypeConverter}.
*
* @param beanFactory the BeanFactory to resolve against
* @param beanName the name of the bean that we work on
* @param beanDefinition the BeanDefinition of the bean that we work on
* @param typeConverter the TypeConverter to use for resolving TypedStringValues
*/
public BeanDefinitionValueResolver(AbstractAutowireCapableBeanFactory beanFactory, String beanName,
BeanDefinition beanDefinition, TypeConverter typeConverter) {
this.beanFactory = beanFactory;
this.beanName = beanName;
this.beanDefinition = beanDefinition;
this.typeConverter = typeConverter;
}
/**
* Create a BeanDefinitionValueResolver for the given BeanFactory and BeanDefinition
* using a default {@link TypeConverter}.
*
* @param beanFactory the BeanFactory to resolve against
* @param beanName the name of the bean that we work on
* @param beanDefinition the BeanDefinition of the bean that we work on
*/
public BeanDefinitionValueResolver(AbstractAutowireCapableBeanFactory beanFactory, String beanName,
BeanDefinition beanDefinition) {
this.beanFactory = beanFactory;
this.beanName = beanName;
this.beanDefinition = beanDefinition;
BeanWrapper beanWrapper = new BeanWrapperImpl();
beanFactory.initBeanWrapper(beanWrapper);
this.typeConverter = beanWrapper;
}
/**
* Given a PropertyValue, return a value, resolving any references to other
* beans in the factory if necessary. The value could be:
*
A BeanDefinition, which leads to the creation of a corresponding
* new bean instance. Singleton flags and names of such "inner beans"
* are always ignored: Inner beans are anonymous prototypes.
* A RuntimeBeanReference, which must be resolved.
* A ManagedList. This is a special collection that may contain
* RuntimeBeanReferences or Collections that will need to be resolved.
* A ManagedSet. May also contain RuntimeBeanReferences or
* Collections that will need to be resolved.
* A ManagedMap. In this case the value may be a RuntimeBeanReference
* or Collection that will need to be resolved.
* An ordinary object or {@code null}, in which case it's left alone.
*
* @param argName the name of the argument that the value is defined for
* @param value the value object to resolve
* @return the resolved object
*/
@Nullable
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
if (value == null) {
return null;
}
else if (value instanceof DependencyDescriptor dependencyDescriptor) {
var autowiredBeanNames = new LinkedHashSet(2);
Object result = beanFactory.resolveDependency(
dependencyDescriptor, beanName, autowiredBeanNames, typeConverter);
for (String autowiredBeanName : autowiredBeanNames) {
if (beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, beanName);
}
}
return result;
}
else if (value instanceof BeanMetadataElement) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
if (value instanceof RuntimeBeanReference ref) {
return resolveReference(argName, ref);
}
else if (value instanceof RuntimeBeanNameReference ref) {
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (!this.beanFactory.containsBean(refName)) {
throw new BeanDefinitionStoreException(
"Invalid bean name '" + refName + "' in bean reference for " + argName);
}
return refName;
}
else if (value instanceof BeanDefinitionHolder bdHolder) {
// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
else if (value instanceof BeanDefinition bd) {
// Resolve plain BeanDefinition, without contained name: use dummy name.
String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
ObjectUtils.getIdentityHexString(bd);
return resolveInnerBean(argName, innerBeanName, bd);
}
else if (value instanceof TypedStringValue typedStringValue) {
// Convert value to target type here.
Object valueObject = evaluate(typedStringValue);
try {
Class> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
return typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
}
else {
return valueObject;
}
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
beanDefinition.getResourceDescription(), beanName,
"Error converting typed String value for " + argName, ex);
}
}
else if (value instanceof ManagedArray managedArray) {
// May need to resolve contained runtime references.
Class> elementType = managedArray.resolvedElementType;
if (elementType == null) {
String elementTypeName = managedArray.getElementTypeName();
if (StringUtils.hasText(elementTypeName)) {
try {
elementType = ClassUtils.forName(elementTypeName, beanFactory.getBeanClassLoader());
managedArray.resolvedElementType = elementType;
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
beanDefinition.getResourceDescription(), beanName,
"Error resolving array type for " + argName, ex);
}
}
else {
elementType = Object.class;
}
}
return resolveManagedArray(argName, (List>) value, elementType);
}
else if (value instanceof ManagedList> managedList) {
// May need to resolve contained runtime references.
return resolveManagedList(argName, managedList);
}
else if (value instanceof ManagedSet> managedSet) {
// May need to resolve contained runtime references.
return resolveManagedSet(argName, managedSet);
}
else if (value instanceof ManagedMap, ?> managedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, managedMap);
}
else if (value instanceof ManagedProperties original) {
// Properties original = managedProperties;
Properties copy = new Properties();
for (Map.Entry