Merge branch '6.0.x'

This commit is contained in:
Juergen Hoeller 2023-07-15 14:21:05 +02:00
commit 351a200747
15 changed files with 355 additions and 157 deletions

View File

@ -705,7 +705,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
@ -724,8 +724,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
}
}
this.cachedFieldValue = cachedFieldValue;
@ -805,7 +804,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
int argumentCount = method.getParameterCount();
Object[] arguments = new Object[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");
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
@ -814,7 +813,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
@ -829,16 +828,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == argumentCount) {
Iterator<String> it = autowiredBeanNames.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (arguments[i] != null && beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
descriptors[i], autowiredBeanName);
}
}
}
@ -864,17 +863,14 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
private final String shortcut;
private final Class<?> requiredType;
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) {
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
super(original);
this.shortcut = shortcut;
this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
return beanFactory.getBean(this.shortcut, this.requiredType);
return beanFactory.getBean(this.shortcut, getDependencyType());
}
}

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -62,20 +62,14 @@ abstract class AutowiredElementResolver {
private final String shortcut;
private final Class<?> requiredType;
public ShortcutDependencyDescriptor(DependencyDescriptor original,
String shortcut, Class<?> requiredType) {
public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut) {
super(original);
this.shortcut = shortcut;
this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
return beanFactory.getBean(this.shortcut, this.requiredType);
return beanFactory.getBean(this.shortcut, getDependencyType());
}
}

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -179,8 +179,7 @@ public final class AutowiredFieldValueResolver extends AutowiredElementResolver
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
descriptor.setContainingClass(beanClass);
if (this.shortcut != null) {
descriptor = new ShortcutDependencyDescriptor(descriptor, this.shortcut,
field.getType());
descriptor = new ShortcutDependencyDescriptor(descriptor, this.shortcut);
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -76,6 +76,7 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
this.shortcuts = shortcuts;
}
/**
* Create a new {@link AutowiredMethodArgumentsResolver} for the specified
* method where injection is optional.
@ -83,11 +84,8 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
* @param parameterTypes the factory method parameter types
* @return a new {@link AutowiredFieldValueResolver} instance
*/
public static AutowiredMethodArgumentsResolver forMethod(String methodName,
Class<?>... parameterTypes) {
return new AutowiredMethodArgumentsResolver(methodName, parameterTypes, false,
null);
public static AutowiredMethodArgumentsResolver forMethod(String methodName, Class<?>... parameterTypes) {
return new AutowiredMethodArgumentsResolver(methodName, parameterTypes, false, null);
}
/**
@ -97,11 +95,8 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
* @param parameterTypes the factory method parameter types
* @return a new {@link AutowiredFieldValueResolver} instance
*/
public static AutowiredMethodArgumentsResolver forRequiredMethod(String methodName,
Class<?>... parameterTypes) {
return new AutowiredMethodArgumentsResolver(methodName, parameterTypes, true,
null);
public static AutowiredMethodArgumentsResolver forRequiredMethod(String methodName, Class<?>... parameterTypes) {
return new AutowiredMethodArgumentsResolver(methodName, parameterTypes, true, null);
}
/**
@ -113,8 +108,7 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
* the shortcuts
*/
public AutowiredMethodArgumentsResolver withShortcut(String... beanNames) {
return new AutowiredMethodArgumentsResolver(this.methodName, this.parameterTypes,
this.required, beanNames);
return new AutowiredMethodArgumentsResolver(this.methodName, this.parameterTypes, this.required, beanNames);
}
/**
@ -123,9 +117,7 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
* @param registeredBean the registered bean
* @param action the action to execute with the resolved method arguments
*/
public void resolve(RegisteredBean registeredBean,
ThrowingConsumer<AutowiredArguments> action) {
public void resolve(RegisteredBean registeredBean, ThrowingConsumer<AutowiredArguments> action) {
Assert.notNull(registeredBean, "'registeredBean' must not be null");
Assert.notNull(action, "'action' must not be null");
AutowiredArguments resolved = resolve(registeredBean);
@ -177,25 +169,22 @@ public final class AutowiredMethodArgumentsResolver extends AutowiredElementReso
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < argumentCount; i++) {
MethodParameter parameter = new MethodParameter(method, i);
DependencyDescriptor descriptor = new DependencyDescriptor(parameter,
this.required);
DependencyDescriptor descriptor = new DependencyDescriptor(parameter, this.required);
descriptor.setContainingClass(beanClass);
String shortcut = (this.shortcuts != null) ? this.shortcuts[i] : null;
String shortcut = (this.shortcuts != null ? this.shortcuts[i] : null);
if (shortcut != null) {
descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut,
parameter.getParameterType());
descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut);
}
try {
Object argument = autowireCapableBeanFactory.resolveDependency(descriptor,
beanName, autowiredBeanNames, typeConverter);
Object argument = autowireCapableBeanFactory.resolveDependency(
descriptor, beanName, autowiredBeanNames, typeConverter);
if (argument == null && !this.required) {
return null;
}
arguments[i] = argument;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName,
new InjectionPoint(parameter), ex);
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex);
}
}
registerDependentBeans(beanFactory, beanName, autowiredBeanNames);

View File

@ -249,18 +249,18 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
() -> "'shortcuts' must contain " + resolved.length + " elements");
ConstructorArgumentValues argumentValues = resolveArgumentValues(registeredBean);
Set<String> autowiredBeans = new LinkedHashSet<>(resolved.length);
Set<String> autowiredBeanNames = new LinkedHashSet<>(resolved.length * 2);
for (int i = startIndex; i < parameterCount; i++) {
MethodParameter parameter = getMethodParameter(executable, i);
DependencyDescriptor descriptor = new DependencyDescriptor(parameter, true);
String shortcut = (this.shortcuts != null ? this.shortcuts[i - startIndex] : null);
if (shortcut != null) {
descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut, registeredBean.getBeanClass());
descriptor = new ShortcutDependencyDescriptor(descriptor, shortcut);
}
ValueHolder argumentValue = argumentValues.getIndexedArgumentValue(i, null);
resolved[i - startIndex] = resolveArgument(registeredBean, descriptor, argumentValue, autowiredBeans);
resolved[i - startIndex] = resolveArgument(registeredBean, descriptor, argumentValue, autowiredBeanNames);
}
registerDependentBeans(registeredBean.getBeanFactory(), registeredBean.getBeanName(), autowiredBeans);
registerDependentBeans(registeredBean.getBeanFactory(), registeredBean.getBeanName(), autowiredBeanNames);
return AutowiredArguments.of(resolved);
}
@ -303,7 +303,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
@Nullable
private Object resolveArgument(RegisteredBean registeredBean, DependencyDescriptor descriptor,
@Nullable ValueHolder argumentValue, Set<String> autowiredBeans) {
@Nullable ValueHolder argumentValue, Set<String> autowiredBeanNames) {
TypeConverter typeConverter = registeredBean.getBeanFactory().getTypeConverter();
if (argumentValue != null) {
@ -312,7 +312,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
descriptor.getDependencyType(), descriptor.getMethodParameter()));
}
try {
return registeredBean.resolveAutowiredArgument(descriptor, typeConverter, autowiredBeans);
return registeredBean.resolveAutowiredArgument(descriptor, typeConverter, autowiredBeanNames);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, registeredBean.getBeanName(), descriptor, ex);

View File

@ -1484,8 +1484,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
converter = bw;
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
Set<String> autowiredBeanNames = new LinkedHashSet<>(propertyNames.length * 2);
for (String propertyName : propertyNames) {
try {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);

View File

@ -153,7 +153,7 @@ public class BeanDefinitionValueResolver {
(name, mbd) -> resolveInnerBeanValue(argName, name, mbd));
}
else if (value instanceof DependencyDescriptor dependencyDescriptor) {
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Object result = this.beanFactory.resolveDependency(
dependencyDescriptor, this.beanName, autowiredBeanNames, this.typeConverter);
for (String autowiredBeanName : autowiredBeanNames) {

View File

@ -48,6 +48,7 @@ import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@ -85,7 +86,7 @@ import org.springframework.util.StringUtils;
* @author Sebastien Deleuze
* @author Sam Brannen
* @author Stephane Nicoll
* @author Phil Webb
* @author Phillip Webb
* @since 2.0
* @see #autowireConstructor
* @see #instantiateUsingFactoryMethod
@ -96,12 +97,6 @@ class ConstructorResolver {
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 =
new NamedThreadLocal<>("Current injection point");
@ -729,7 +724,7 @@ class ConstructorResolver {
ArgumentsHolder args = new ArgumentsHolder(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++) {
Class<?> paramType = paramTypes[paramIndex];
@ -764,8 +759,8 @@ class ConstructorResolver {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" +
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
Object sourceHolder = valueHolder.getSource();
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder constructorValueHolder) {
@ -788,11 +783,17 @@ class ConstructorResolver {
"] - did you specify the correct bean references as arguments?");
}
try {
Object autowiredArgument = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
beanName, autowiredBeanNames, converter, fallback);
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
ConstructorDependencyDescriptor desc = new ConstructorDependencyDescriptor(methodParam, true);
Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
Object arg = resolveAutowiredArgument(
desc, paramType, beanName, autowiredBeanNames, converter, fallback);
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;
}
catch (BeansException ex) {
@ -802,14 +803,7 @@ class ConstructorResolver {
}
}
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 + "'");
}
}
registerDependentBeans(executable, beanName, allAutowiredBeanNames);
return args;
}
@ -829,32 +823,56 @@ class ConstructorResolver {
Object[] resolvedArgs = new Object[argsToResolve.length];
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
Object argValue = argsToResolve[argIndex];
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
if (argValue == autowiredArgumentMarker) {
argValue = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
beanName, null, converter, true);
Class<?> paramType = paramTypes[argIndex];
boolean convertNecessary = false;
if (argValue instanceof ConstructorDependencyDescriptor descriptor) {
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) {
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
convertNecessary = true;
}
else if (argValue instanceof String text) {
argValue = this.beanFactory.evaluateBeanDefinitionString(text, mbd);
convertNecessary = true;
}
Class<?> paramType = paramTypes[argIndex];
try {
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
if (convertNecessary) {
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
try {
argValue = converter.convertIfNecessary(argValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
throw new UnsatisfiedDependencyException(
mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
"Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
"] to required type [" + paramType.getName() + "]: " + ex.getMessage());
}
}
resolvedArgs[argIndex] = argValue;
}
return resolvedArgs;
}
protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
private Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) {
Class<?> declaringClass = constructor.getDeclaringClass();
Class<?> userClass = ClassUtils.getUserClass(declaringClass);
if (userClass != declaringClass) {
@ -870,13 +888,12 @@ 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
protected Object resolveAutowiredArgument(DependencyDescriptor descriptor, String beanName,
Object resolveAutowiredArgument(DependencyDescriptor descriptor, Class<?> paramType, String beanName,
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
Class<?> paramType = descriptor.getMethodParameter().getParameterType();
if (InjectionPoint.class.isAssignableFrom(paramType)) {
InjectionPoint injectionPoint = currentInjectionPoint.get();
if (injectionPoint == null) {
@ -884,9 +901,9 @@ class ConstructorResolver {
}
return injectionPoint;
}
try {
return this.beanFactory.resolveDependency(
descriptor, beanName, autowiredBeanNames, typeConverter);
return this.beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
}
catch (NoUniqueBeanDefinitionException ex) {
throw ex;
@ -909,6 +926,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 + "'");
}
}
}
// AOT-oriented pre-resolution
@ -1361,6 +1403,37 @@ 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);
}
}
private enum FallbackMode {
NONE,

View File

@ -216,16 +216,18 @@ public final class RegisteredBean {
* Resolve an autowired argument.
* @param descriptor the descriptor for the dependency (field/method/constructor)
* @param typeConverter the TypeConverter to use for populating arrays and collections
* @param autowiredBeans a Set that all names of autowired beans (used for
* @param autowiredBeanNames a Set that all names of autowired beans (used for
* resolving the given dependency) are supposed to be added to
* @return the resolved object, or {@code null} if none found
* @since 6.0.9
*/
@Nullable
public Object resolveAutowiredArgument(DependencyDescriptor descriptor, TypeConverter typeConverter,
Set<String> autowiredBeans) {
public Object resolveAutowiredArgument(
DependencyDescriptor descriptor, TypeConverter typeConverter, Set<String> autowiredBeanNames) {
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
.resolveAutowiredArgument(descriptor, getBeanName(), autowiredBeans, typeConverter, true);
.resolveAutowiredArgument(descriptor, descriptor.getDependencyType(),
getBeanName(), autowiredBeanNames, typeConverter, true);
}

View File

@ -71,6 +71,8 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
@ -128,6 +130,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bean = bf.getBean("annotatedBean", ResourceInjectionBean.class);
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
}
@Test
@ -150,10 +154,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
}
@Test
void resourceInjectionWithSometimesNullBean() {
void resourceInjectionWithSometimesNullBeanEarly() {
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
@ -168,6 +174,55 @@ public class AutowiredAnnotationBeanPostProcessorTests {
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();
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;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
@ -192,17 +247,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
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();
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean"});
}
@Test
@ -231,10 +282,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
assertThat(depBeans).hasSize(2);
assertThat(depBeans[0]).isEqualTo("testBean");
assertThat(depBeans[1]).isEqualTo("nestedTestBean");
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(new String[] {"testBean", "nestedTestBean"});
}
@Test
@ -696,6 +744,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
assertThat(bf.getDependenciesForBean("annotatedBean")).isEqualTo(
new String[] {"testBean", "nestedTestBean", ObjectUtils.identityToString(bf)});
}
@Test
@ -858,6 +909,80 @@ public class AutowiredAnnotationBeanPostProcessorTests {
.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
void constructorResourceInjectionWithCollectionAndNullFromFactoryBean() {
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
@ -2788,6 +2913,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 {
protected ITestBean testBean3;

View File

@ -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");
* 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,
* e.g. "example.MyExampleClass.myExampleMethod".
* Convenient alternative to specifying targetClass and targetMethod.
* e.g. "example.MyExampleClass.myExampleMethod". This is a
* convenient alternative to specifying targetClass and targetMethod.
* @see #setTargetClass
* @see #setTargetMethod
*/
@ -157,14 +157,16 @@ public class MethodInvoker {
public void prepare() throws ClassNotFoundException, NoSuchMethodException {
if (this.staticMethod != null) {
int lastDotIndex = this.staticMethod.lastIndexOf('.');
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length()) {
if (lastDotIndex == -1 || lastDotIndex == this.staticMethod.length() - 1) {
throw new IllegalArgumentException(
"staticMethod must be a fully qualified class plus method name: " +
"e.g. 'example.MyExampleClass.myExampleMethod'");
}
String className = this.staticMethod.substring(0, lastDotIndex);
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;
}

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -33,57 +33,56 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class ByteBufferConverterTests {
private GenericConversionService conversionService;
private final GenericConversionService conversionService = new DefaultConversionService();
@BeforeEach
void setup() {
this.conversionService = new DefaultConversionService();
this.conversionService.addConverter(new ByteArrayToOtherTypeConverter());
this.conversionService.addConverter(new OtherTypeToByteArrayConverter());
conversionService.addConverter(new ByteArrayToOtherTypeConverter());
conversionService.addConverter(new OtherTypeToByteArrayConverter());
}
@Test
void byteArrayToByteBuffer() throws Exception {
void byteArrayToByteBuffer() {
byte[] bytes = new byte[] { 1, 2, 3 };
ByteBuffer convert = this.conversionService.convert(bytes, ByteBuffer.class);
ByteBuffer convert = conversionService.convert(bytes, ByteBuffer.class);
assertThat(convert.array()).isNotSameAs(bytes);
assertThat(convert.array()).isEqualTo(bytes);
}
@Test
void byteBufferToByteArray() throws Exception {
void byteBufferToByteArray() {
byte[] bytes = new byte[] { 1, 2, 3 };
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byte[] convert = this.conversionService.convert(byteBuffer, byte[].class);
byte[] convert = conversionService.convert(byteBuffer, byte[].class);
assertThat(convert).isNotSameAs(bytes);
assertThat(convert).isEqualTo(bytes);
}
@Test
void byteBufferToOtherType() throws Exception {
void byteBufferToOtherType() {
byte[] bytes = new byte[] { 1, 2, 3 };
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
OtherType convert = this.conversionService.convert(byteBuffer, OtherType.class);
OtherType convert = conversionService.convert(byteBuffer, OtherType.class);
assertThat(convert.bytes).isNotSameAs(bytes);
assertThat(convert.bytes).isEqualTo(bytes);
}
@Test
void otherTypeToByteBuffer() throws Exception {
void otherTypeToByteBuffer() {
byte[] bytes = new byte[] { 1, 2, 3 };
OtherType otherType = new OtherType(bytes);
ByteBuffer convert = this.conversionService.convert(otherType, ByteBuffer.class);
ByteBuffer convert = conversionService.convert(otherType, ByteBuffer.class);
assertThat(convert.array()).isNotSameAs(bytes);
assertThat(convert.array()).isEqualTo(bytes);
}
@Test
void byteBufferToByteBuffer() throws Exception {
void byteBufferToByteBuffer() {
byte[] bytes = new byte[] { 1, 2, 3 };
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
ByteBuffer convert = this.conversionService.convert(byteBuffer, ByteBuffer.class);
ByteBuffer convert = conversionService.convert(byteBuffer, ByteBuffer.class);
assertThat(convert).isNotSameAs(byteBuffer.rewind());
assertThat(convert).isEqualTo(byteBuffer.rewind());
assertThat(convert).isEqualTo(ByteBuffer.wrap(bytes));

View File

@ -49,11 +49,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
class CollectionToCollectionConverterTests {
private GenericConversionService conversionService = new GenericConversionService();
private final GenericConversionService conversionService = new GenericConversionService();
@BeforeEach
void setUp() {
void setup() {
conversionService.addConverter(new CollectionToCollectionConverter(conversionService));
}

View File

@ -38,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Keith Donald
* @author Phil Webb
* @author Phillip Webb
* @author Juergen Hoeller
*/
class MapToMapConverterTests {
@ -47,7 +47,7 @@ class MapToMapConverterTests {
@BeforeEach
void setUp() {
void setup() {
conversionService.addConverter(new MapToMapConverter(conversionService));
}

View File

@ -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");
* you may not use this file except in compliance with the License.
@ -18,6 +18,7 @@ package org.springframework.core.convert.support;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.ConverterNotFoundException;
@ -29,15 +30,19 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* Unit tests for {@link ObjectToObjectConverter}.
*
* @author Sam Brannen
* @author Phil Webb
* @author Phillip Webb
* @since 5.3.21
* @see org.springframework.core.convert.converter.DefaultConversionServiceTests#convertObjectToObjectUsingValueOfMethod()
*/
class ObjectToObjectConverterTests {
private final GenericConversionService conversionService = new GenericConversionService() {{
addConverter(new ObjectToObjectConverter());
}};
private final GenericConversionService conversionService = new GenericConversionService();
@BeforeEach
void setup() {
conversionService.addConverter(new ObjectToObjectConverter());
}
/**
@ -47,7 +52,7 @@ class ObjectToObjectConverterTests {
@Test
void nonStaticToTargetTypeSimpleNameMethodWithMatchingReturnType() {
assertThat(conversionService.canConvert(Source.class, Data.class))
.as("can convert Source to Data").isTrue();
.as("can convert Source to Data").isTrue();
Data data = conversionService.convert(new Source("test"), Data.class);
assertThat(data).asString().isEqualTo("test");
}
@ -55,21 +60,21 @@ class ObjectToObjectConverterTests {
@Test
void nonStaticToTargetTypeSimpleNameMethodWithDifferentReturnType() {
assertThat(conversionService.canConvert(Text.class, Data.class))
.as("can convert Text to Data").isFalse();
.as("can convert Text to Data").isFalse();
assertThat(conversionService.canConvert(Text.class, Optional.class))
.as("can convert Text to Optional").isFalse();
.as("can convert Text to Optional").isFalse();
assertThatExceptionOfType(ConverterNotFoundException.class)
.as("convert Text to Data")
.isThrownBy(() -> conversionService.convert(new Text("test"), Data.class));
.as("convert Text to Data")
.isThrownBy(() -> conversionService.convert(new Text("test"), Data.class));
}
@Test
void staticValueOfFactoryMethodWithDifferentReturnType() {
assertThat(conversionService.canConvert(String.class, Data.class))
.as("can convert String to Data").isFalse();
.as("can convert String to Data").isFalse();
assertThatExceptionOfType(ConverterNotFoundException.class)
.as("convert String to Data")
.isThrownBy(() -> conversionService.convert("test", Data.class));
.as("convert String to Data")
.isThrownBy(() -> conversionService.convert("test", Data.class));
}
@ -84,9 +89,9 @@ class ObjectToObjectConverterTests {
public Data toData() {
return new Data(this.value);
}
}
static class Text {
private final String value;
@ -98,9 +103,9 @@ class ObjectToObjectConverterTests {
public Optional<Data> toData() {
return Optional.of(new Data(this.value));
}
}
static class Data {
private final String value;
@ -115,9 +120,8 @@ class ObjectToObjectConverterTests {
}
public static Optional<Data> valueOf(String string) {
return (string != null) ? Optional.of(new Data(string)) : Optional.empty();
return (string != null ? Optional.of(new Data(string)) : Optional.empty());
}
}
}