Cache DependencyDescriptor per autowired constructor argument
Aligned with shortcut handling in AutowiredAnnotationBeanPostProcessor.
Includes minor MethodInvoker optimization for pre-resolved targetClass.
Closes gh-30883
(cherry picked from commit 6183f06846)
This commit is contained in:
@@ -651,7 +651,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
|
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
|
||||||
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
|
||||||
desc.setContainingClass(bean.getClass());
|
desc.setContainingClass(bean.getClass());
|
||||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||||
Object value;
|
Object value;
|
||||||
@@ -670,8 +670,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||||
if (beanFactory.containsBean(autowiredBeanName) &&
|
if (beanFactory.containsBean(autowiredBeanName) &&
|
||||||
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
|
||||||
cachedFieldValue = new ShortcutDependencyDescriptor(
|
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
|
||||||
desc, autowiredBeanName, field.getType());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.cachedFieldValue = cachedFieldValue;
|
this.cachedFieldValue = cachedFieldValue;
|
||||||
@@ -754,7 +753,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
int argumentCount = method.getParameterCount();
|
int argumentCount = method.getParameterCount();
|
||||||
Object[] arguments = new Object[argumentCount];
|
Object[] arguments = new Object[argumentCount];
|
||||||
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
|
||||||
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(argumentCount * 2);
|
||||||
Assert.state(beanFactory != null, "No BeanFactory available");
|
Assert.state(beanFactory != null, "No BeanFactory available");
|
||||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||||
for (int i = 0; i < arguments.length; i++) {
|
for (int i = 0; i < arguments.length; i++) {
|
||||||
@@ -763,7 +762,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
currDesc.setContainingClass(bean.getClass());
|
currDesc.setContainingClass(bean.getClass());
|
||||||
descriptors[i] = currDesc;
|
descriptors[i] = currDesc;
|
||||||
try {
|
try {
|
||||||
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
|
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
|
||||||
if (arg == null && !this.required) {
|
if (arg == null && !this.required) {
|
||||||
arguments = null;
|
arguments = null;
|
||||||
break;
|
break;
|
||||||
@@ -778,16 +777,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
if (!this.cached) {
|
if (!this.cached) {
|
||||||
if (arguments != null) {
|
if (arguments != null) {
|
||||||
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
|
||||||
registerDependentBeans(beanName, autowiredBeans);
|
registerDependentBeans(beanName, autowiredBeanNames);
|
||||||
if (autowiredBeans.size() == argumentCount) {
|
if (autowiredBeanNames.size() == argumentCount) {
|
||||||
Iterator<String> it = autowiredBeans.iterator();
|
Iterator<String> it = autowiredBeanNames.iterator();
|
||||||
Class<?>[] paramTypes = method.getParameterTypes();
|
Class<?>[] paramTypes = method.getParameterTypes();
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
String autowiredBeanName = it.next();
|
String autowiredBeanName = it.next();
|
||||||
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
|
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
|
||||||
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
|
||||||
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
|
||||||
descriptors[i], autowiredBeanName, paramTypes[i]);
|
descriptors[i], autowiredBeanName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,17 +812,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||||||
|
|
||||||
private final String shortcut;
|
private final String shortcut;
|
||||||
|
|
||||||
private final Class<?> requiredType;
|
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
|
||||||
|
|
||||||
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
|
|
||||||
super(original);
|
super(original);
|
||||||
this.shortcut = shortcut;
|
this.shortcut = shortcut;
|
||||||
this.requiredType = requiredType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||||
return beanFactory.getBean(this.shortcut, this.requiredType);
|
return beanFactory.getBean(this.shortcut, getDependencyType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -1504,8 +1504,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
converter = bw;
|
converter = bw;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
|
||||||
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
|
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
|
||||||
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
|
||||||
for (String propertyName : propertyNames) {
|
for (String propertyName : propertyNames) {
|
||||||
try {
|
try {
|
||||||
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
|
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -134,7 +134,7 @@ class BeanDefinitionValueResolver {
|
|||||||
return resolveInnerBean(argName, innerBeanName, bd);
|
return resolveInnerBean(argName, innerBeanName, bd);
|
||||||
}
|
}
|
||||||
else if (value instanceof DependencyDescriptor) {
|
else if (value instanceof DependencyDescriptor) {
|
||||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||||
Object result = this.beanFactory.resolveDependency(
|
Object result = this.beanFactory.resolveDependency(
|
||||||
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
|
(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
|
||||||
for (String autowiredBeanName : autowiredBeanNames) {
|
for (String autowiredBeanName : autowiredBeanNames) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -45,6 +45,7 @@ import org.springframework.beans.TypeConverter;
|
|||||||
import org.springframework.beans.TypeMismatchException;
|
import org.springframework.beans.TypeMismatchException;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.InjectionPoint;
|
import org.springframework.beans.factory.InjectionPoint;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||||
@@ -85,12 +86,6 @@ class ConstructorResolver {
|
|||||||
|
|
||||||
private static final Object[] EMPTY_ARGS = new Object[0];
|
private static final Object[] EMPTY_ARGS = new Object[0];
|
||||||
|
|
||||||
/**
|
|
||||||
* Marker for autowired arguments in a cached argument array, to be replaced
|
|
||||||
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
|
|
||||||
*/
|
|
||||||
private static final Object autowiredArgumentMarker = new Object();
|
|
||||||
|
|
||||||
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
|
private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint =
|
||||||
new NamedThreadLocal<>("Current injection point");
|
new NamedThreadLocal<>("Current injection point");
|
||||||
|
|
||||||
@@ -729,7 +724,7 @@ class ConstructorResolver {
|
|||||||
|
|
||||||
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
||||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
||||||
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
|
Set<String> allAutowiredBeanNames = new LinkedHashSet<>(paramTypes.length * 2);
|
||||||
|
|
||||||
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
||||||
Class<?> paramType = paramTypes[paramIndex];
|
Class<?> paramType = paramTypes[paramIndex];
|
||||||
@@ -764,8 +759,8 @@ class ConstructorResolver {
|
|||||||
throw new UnsatisfiedDependencyException(
|
throw new UnsatisfiedDependencyException(
|
||||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||||
"Could not convert argument value of type [" +
|
"Could not convert argument value of type [" +
|
||||||
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
|
||||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||||
}
|
}
|
||||||
Object sourceHolder = valueHolder.getSource();
|
Object sourceHolder = valueHolder.getSource();
|
||||||
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
|
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
|
||||||
@@ -788,11 +783,17 @@ class ConstructorResolver {
|
|||||||
"] - did you specify the correct bean references as arguments?");
|
"] - did you specify the correct bean references as arguments?");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Object autowiredArgument = resolveAutowiredArgument(
|
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor(methodParam, true);
|
||||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||||
args.rawArguments[paramIndex] = autowiredArgument;
|
Object arg = resolveAutowiredArgument(
|
||||||
args.arguments[paramIndex] = autowiredArgument;
|
desc, paramType, beanName, autowiredBeanNames, converter, fallback);
|
||||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
if (arg != null) {
|
||||||
|
setShortcutIfPossible(desc, paramType, autowiredBeanNames);
|
||||||
|
}
|
||||||
|
allAutowiredBeanNames.addAll(autowiredBeanNames);
|
||||||
|
args.rawArguments[paramIndex] = arg;
|
||||||
|
args.arguments[paramIndex] = arg;
|
||||||
|
args.preparedArguments[paramIndex] = desc;
|
||||||
args.resolveNecessary = true;
|
args.resolveNecessary = true;
|
||||||
}
|
}
|
||||||
catch (BeansException ex) {
|
catch (BeansException ex) {
|
||||||
@@ -802,14 +803,7 @@ class ConstructorResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String autowiredBeanName : autowiredBeanNames) {
|
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
|
||||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Autowiring by type from bean name '" + beanName +
|
|
||||||
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
|
|
||||||
" to bean named '" + autowiredBeanName + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@@ -829,31 +823,57 @@ class ConstructorResolver {
|
|||||||
Object[] resolvedArgs = new Object[argsToResolve.length];
|
Object[] resolvedArgs = new Object[argsToResolve.length];
|
||||||
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
|
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
|
||||||
Object argValue = argsToResolve[argIndex];
|
Object argValue = argsToResolve[argIndex];
|
||||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
Class<?> paramType = paramTypes[argIndex];
|
||||||
if (argValue == autowiredArgumentMarker) {
|
boolean convertNecessary = false;
|
||||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
if (argValue instanceof ConstructorDependencyDescriptor) {
|
||||||
|
ConstructorDependencyDescriptor descriptor = (ConstructorDependencyDescriptor) argValue;
|
||||||
|
try {
|
||||||
|
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||||
|
null, converter, true);
|
||||||
|
}
|
||||||
|
catch (BeansException ex) {
|
||||||
|
// Unexpected target bean mismatch for cached argument -> re-resolve
|
||||||
|
synchronized (descriptor) {
|
||||||
|
if (!descriptor.hasShortcut()) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
descriptor.setShortcut(null);
|
||||||
|
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
|
||||||
|
argValue = resolveAutowiredArgument(descriptor, paramType, beanName,
|
||||||
|
autowiredBeanNames, converter, true);
|
||||||
|
if (argValue != null) {
|
||||||
|
setShortcutIfPossible(descriptor, paramType, autowiredBeanNames);
|
||||||
|
}
|
||||||
|
registerDependentBeans(executable, beanName, autowiredBeanNames);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (argValue instanceof BeanMetadataElement) {
|
else if (argValue instanceof BeanMetadataElement) {
|
||||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||||
|
convertNecessary = true;
|
||||||
}
|
}
|
||||||
else if (argValue instanceof String) {
|
else if (argValue instanceof String) {
|
||||||
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
|
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
|
||||||
|
convertNecessary = true;
|
||||||
}
|
}
|
||||||
Class<?> paramType = paramTypes[argIndex];
|
if (convertNecessary) {
|
||||||
try {
|
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||||
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
|
try {
|
||||||
}
|
argValue = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||||
catch (TypeMismatchException ex) {
|
}
|
||||||
throw new UnsatisfiedDependencyException(
|
catch (TypeMismatchException ex) {
|
||||||
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
throw new UnsatisfiedDependencyException(
|
||||||
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
|
||||||
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
|
||||||
|
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
resolvedArgs[argIndex] = argValue;
|
||||||
}
|
}
|
||||||
return resolvedArgs;
|
return resolvedArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
|
||||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||||
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
|
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
|
||||||
if (userClass != declaringClass) {
|
if (userClass != declaringClass) {
|
||||||
@@ -869,23 +889,22 @@ class ConstructorResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method for resolving the specified argument which is supposed to be autowired.
|
* Resolve the specified argument which is supposed to be autowired.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
|
||||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||||
|
|
||||||
Class<?> paramType = param.getParameterType();
|
|
||||||
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
||||||
InjectionPoint injectionPoint = currentInjectionPoint.get();
|
InjectionPoint injectionPoint = currentInjectionPoint.get();
|
||||||
if (injectionPoint == null) {
|
if (injectionPoint == null) {
|
||||||
throw new IllegalStateException("No current InjectionPoint available for " + param);
|
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
|
||||||
}
|
}
|
||||||
return injectionPoint;
|
return injectionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return this.beanFactory.resolveDependency(
|
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
|
||||||
}
|
}
|
||||||
catch (NoUniqueBeanDefinitionException ex) {
|
catch (NoUniqueBeanDefinitionException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -908,6 +927,31 @@ class ConstructorResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setShortcutIfPossible(
|
||||||
|
ConstructorDependencyDescriptor descriptor, Class<?> paramType, Set<String> autowiredBeanNames) {
|
||||||
|
|
||||||
|
if (autowiredBeanNames.size() == 1) {
|
||||||
|
String autowiredBeanName = autowiredBeanNames.iterator().next();
|
||||||
|
if (this.beanFactory.containsBean(autowiredBeanName) &&
|
||||||
|
this.beanFactory.isTypeMatch(autowiredBeanName, paramType)) {
|
||||||
|
descriptor.setShortcut(autowiredBeanName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerDependentBeans(
|
||||||
|
Executable executable, String beanName, Set<String> autowiredBeanNames) {
|
||||||
|
|
||||||
|
for (String autowiredBeanName : autowiredBeanNames) {
|
||||||
|
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Autowiring by type from bean name '" + beanName + "' via " +
|
||||||
|
(executable instanceof Constructor ? "constructor" : "factory method") +
|
||||||
|
" to bean named '" + autowiredBeanName + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
|
static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
|
||||||
InjectionPoint old = currentInjectionPoint.get();
|
InjectionPoint old = currentInjectionPoint.get();
|
||||||
if (injectionPoint != null) {
|
if (injectionPoint != null) {
|
||||||
@@ -1006,4 +1050,35 @@ class ConstructorResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DependencyDescriptor marker for constructor arguments,
|
||||||
|
* for differentiating between a provided DependencyDescriptor instance
|
||||||
|
* and an internally built DependencyDescriptor for autowiring purposes.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
private static class ConstructorDependencyDescriptor extends DependencyDescriptor {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile String shortcut;
|
||||||
|
|
||||||
|
public ConstructorDependencyDescriptor(MethodParameter methodParameter, boolean required) {
|
||||||
|
super(methodParameter, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortcut(@Nullable String shortcut) {
|
||||||
|
this.shortcut = shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasShortcut() {
|
||||||
|
return (this.shortcut != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||||
|
String shortcut = this.shortcut;
|
||||||
|
return (shortcut != null ? beanFactory.getBean(shortcut, getDependencyType()) : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ import org.springframework.core.ResolvableType;
|
|||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -131,6 +133,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
|
bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
|
||||||
assertThat(bean.getTestBean()).isSameAs(tb);
|
assertThat(bean.getTestBean()).isSameAs(tb);
|
||||||
assertThat(bean.getTestBean2()).isSameAs(tb);
|
assertThat(bean.getTestBean2()).isSameAs(tb);
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -152,10 +156,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertThat(bean.getTestBean()).isNull();
|
assertThat(bean.getTestBean()).isNull();
|
||||||
assertThat(bean.getTestBean2()).isNull();
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
assertThat(bean.getTestBean3()).isNull();
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resourceInjectionWithSometimesNullBean() {
|
void resourceInjectionWithSometimesNullBeanEarly() {
|
||||||
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||||
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
bf.registerBeanDefinition("annotatedBean", bd);
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
@@ -170,6 +176,55 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertThat(bean.getTestBean2()).isNull();
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
assertThat(bean.getTestBean3()).isNull();
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void resourceInjectionWithSometimesNullBeanLate() {
|
||||||
|
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
|
||||||
|
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
|
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||||
|
tb.setFactoryMethodName("createTestBean");
|
||||||
|
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("testBean", tb);
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean2()).isNotNull();
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
SometimesNullFactoryMethods.active = true;
|
SometimesNullFactoryMethods.active = true;
|
||||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
assertThat(bean.getTestBean()).isNotNull();
|
assertThat(bean.getTestBean()).isNotNull();
|
||||||
@@ -194,17 +249,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertThat(bean.getTestBean2()).isNotNull();
|
assertThat(bean.getTestBean2()).isNotNull();
|
||||||
assertThat(bean.getTestBean3()).isNotNull();
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
SometimesNullFactoryMethods.active = true;
|
|
||||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
|
||||||
assertThat(bean.getTestBean()).isNotNull();
|
|
||||||
assertThat(bean.getTestBean2()).isNotNull();
|
|
||||||
assertThat(bean.getTestBean3()).isNotNull();
|
|
||||||
|
|
||||||
SometimesNullFactoryMethods.active = false;
|
SometimesNullFactoryMethods.active = false;
|
||||||
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||||
assertThat(bean.getTestBean()).isNull();
|
assertThat(bean.getTestBean()).isNull();
|
||||||
assertThat(bean.getTestBean2()).isNull();
|
assertThat(bean.getTestBean2()).isNull();
|
||||||
assertThat(bean.getTestBean3()).isNull();
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -233,10 +284,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||||
|
|
||||||
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean", "nestedTestBean"});
|
||||||
assertThat(depBeans.length).isEqualTo(2);
|
|
||||||
assertThat(depBeans[0]).isEqualTo("testBean");
|
|
||||||
assertThat(depBeans[1]).isEqualTo("nestedTestBean");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -719,6 +767,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertThat(bean.getTestBean4()).isSameAs(tb);
|
assertThat(bean.getTestBean4()).isSameAs(tb);
|
||||||
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
|
||||||
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
assertThat(bean.getBeanFactory()).isSameAs(bf);
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(
|
||||||
|
new String[] {"testBean", "nestedTestBean", ObjectUtils.identityToString(bf)});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -881,6 +932,80 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
|
.satisfies(methodParameterDeclaredOn(ConstructorWithoutFallbackBean.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorResourceInjectionWithSometimesNullBeanEarly() {
|
||||||
|
RootBeanDefinition bd = new RootBeanDefinition(ConstructorWithNullableArgument.class);
|
||||||
|
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
|
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||||
|
tb.setFactoryMethodName("createTestBean");
|
||||||
|
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("testBean", tb);
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
ConstructorWithNullableArgument bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorResourceInjectionWithSometimesNullBeanLate() {
|
||||||
|
RootBeanDefinition bd = new RootBeanDefinition(ConstructorWithNullableArgument.class);
|
||||||
|
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
|
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
|
||||||
|
tb.setFactoryMethodName("createTestBean");
|
||||||
|
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
bf.registerBeanDefinition("testBean", tb);
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
ConstructorWithNullableArgument bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = true;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNotNull();
|
||||||
|
|
||||||
|
SometimesNullFactoryMethods.active = false;
|
||||||
|
bean = (ConstructorWithNullableArgument) bf.getBean("annotatedBean");
|
||||||
|
assertThat(bean.getTestBean3()).isNull();
|
||||||
|
|
||||||
|
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorResourceInjectionWithCollectionAndNullFromFactoryBean() {
|
public void testConstructorResourceInjectionWithCollectionAndNullFromFactoryBean() {
|
||||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
||||||
@@ -2804,6 +2929,21 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ConstructorWithNullableArgument {
|
||||||
|
|
||||||
|
protected ITestBean testBean3;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
public ConstructorWithNullableArgument(@Nullable ITestBean testBean3) {
|
||||||
|
this.testBean3 = testBean3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITestBean getTestBean3() {
|
||||||
|
return this.testBean3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class ConstructorsCollectionResourceInjectionBean {
|
public static class ConstructorsCollectionResourceInjectionBean {
|
||||||
|
|
||||||
protected ITestBean testBean3;
|
protected ITestBean testBean3;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -123,8 +123,8 @@ public class MethodInvoker {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a fully qualified static method name to invoke,
|
* Set a fully qualified static method name to invoke,
|
||||||
* e.g. "example.MyExampleClass.myExampleMethod".
|
* e.g. "example.MyExampleClass.myExampleMethod". This is a
|
||||||
* Convenient alternative to specifying targetClass and targetMethod.
|
* convenient alternative to specifying targetClass and targetMethod.
|
||||||
* @see #setTargetClass
|
* @see #setTargetClass
|
||||||
* @see #setTargetMethod
|
* @see #setTargetMethod
|
||||||
*/
|
*/
|
||||||
@@ -157,14 +157,16 @@ public class MethodInvoker {
|
|||||||
public void prepare() throws ClassNotFoundException, NoSuchMethodException {
|
public void prepare() throws ClassNotFoundException, NoSuchMethodException {
|
||||||
if (this.staticMethod != null) {
|
if (this.staticMethod != null) {
|
||||||
int lastDotIndex = this.staticMethod.lastIndexOf('.');
|
int lastDotIndex = this.staticMethod.lastIndexOf('.');
|
||||||
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length()) {
|
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length() - 1) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"staticMethod must be a fully qualified class plus method name: " +
|
"staticMethod must be a fully qualified class plus method name: " +
|
||||||
"e.g. 'example.MyExampleClass.myExampleMethod'");
|
"e.g. 'example.MyExampleClass.myExampleMethod'");
|
||||||
}
|
}
|
||||||
String className = this.staticMethod.substring(0, lastDotIndex);
|
String className = this.staticMethod.substring(0, lastDotIndex);
|
||||||
String methodName = this.staticMethod.substring(lastDotIndex + 1);
|
String methodName = this.staticMethod.substring(lastDotIndex + 1);
|
||||||
this.targetClass = resolveClassName(className);
|
if (this.targetClass == null || !this.targetClass.getName().equals(className)) {
|
||||||
|
this.targetClass = resolveClassName(className);
|
||||||
|
}
|
||||||
this.targetMethod = methodName;
|
this.targetMethod = methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user