Skip to content

Commit d2e27ad

Browse files
committed
Support by-type constructor references via RuntimeBeanReference
Closes gh-28728
1 parent a21b27e commit d2e27ad

File tree

2 files changed

+79
-135
lines changed

2 files changed

+79
-135
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/aot/ConstructorOrFactoryMethodResolver.java

Lines changed: 66 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,15 @@
3030
import java.util.function.Predicate;
3131
import java.util.function.Supplier;
3232

33-
import org.apache.commons.logging.Log;
34-
import org.apache.commons.logging.LogFactory;
35-
33+
import org.springframework.beans.BeanUtils;
3634
import org.springframework.beans.factory.FactoryBean;
3735
import org.springframework.beans.factory.annotation.Autowired;
3836
import org.springframework.beans.factory.config.BeanDefinition;
3937
import org.springframework.beans.factory.config.BeanReference;
4038
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4139
import org.springframework.beans.factory.config.ConstructorArgumentValues;
4240
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
41+
import org.springframework.beans.factory.config.RuntimeBeanReference;
4342
import org.springframework.beans.factory.support.AbstractBeanDefinition;
4443
import org.springframework.beans.factory.support.RegisteredBean;
4544
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -62,57 +61,52 @@
6261
*/
6362
class ConstructorOrFactoryMethodResolver {
6463

65-
private static final Log logger = LogFactory
66-
.getLog(ConstructorOrFactoryMethodResolver.class);
67-
68-
6964
private final ConfigurableBeanFactory beanFactory;
7065

66+
@Nullable
7167
private final ClassLoader classLoader;
7268

7369

7470
ConstructorOrFactoryMethodResolver(ConfigurableBeanFactory beanFactory) {
7571
this.beanFactory = beanFactory;
76-
this.classLoader = (beanFactory.getBeanClassLoader() != null)
77-
? beanFactory.getBeanClassLoader() : ClassUtils.getDefaultClassLoader();
72+
this.classLoader = (beanFactory.getBeanClassLoader() != null ?
73+
beanFactory.getBeanClassLoader() : ClassUtils.getDefaultClassLoader());
7874
}
7975

8076

77+
@Nullable
8178
Executable resolve(BeanDefinition beanDefinition) {
8279
Supplier<ResolvableType> beanType = () -> getBeanType(beanDefinition);
83-
List<ResolvableType> valueTypes = beanDefinition.hasConstructorArgumentValues()
84-
? determineParameterValueTypes(
85-
beanDefinition.getConstructorArgumentValues())
86-
: Collections.emptyList();
80+
List<ResolvableType> valueTypes = (beanDefinition.hasConstructorArgumentValues() ?
81+
determineParameterValueTypes(beanDefinition.getConstructorArgumentValues()) :
82+
Collections.emptyList());
8783
Method resolvedFactoryMethod = resolveFactoryMethod(beanDefinition, valueTypes);
8884
if (resolvedFactoryMethod != null) {
8985
return resolvedFactoryMethod;
9086
}
87+
9188
Class<?> factoryBeanClass = getFactoryBeanClass(beanDefinition);
92-
if (factoryBeanClass != null && !factoryBeanClass
93-
.equals(beanDefinition.getResolvableType().toClass())) {
89+
if (factoryBeanClass != null && !factoryBeanClass.equals(beanDefinition.getResolvableType().toClass())) {
9490
ResolvableType resolvableType = beanDefinition.getResolvableType();
9591
boolean isCompatible = ResolvableType.forClass(factoryBeanClass)
9692
.as(FactoryBean.class).getGeneric(0).isAssignableFrom(resolvableType);
97-
Assert.state(isCompatible,
98-
() -> String.format(
99-
"Incompatible target type '%s' for factory bean '%s'",
100-
resolvableType.toClass().getName(),
101-
factoryBeanClass.getName()));
102-
return resolveConstructor(() -> ResolvableType.forClass(factoryBeanClass),
103-
valueTypes);
93+
Assert.state(isCompatible, () -> String.format(
94+
"Incompatible target type '%s' for factory bean '%s'",
95+
resolvableType.toClass().getName(), factoryBeanClass.getName()));
96+
return resolveConstructor(() -> ResolvableType.forClass(factoryBeanClass), valueTypes);
10497
}
98+
10599
Executable resolvedConstructor = resolveConstructor(beanType, valueTypes);
106100
if (resolvedConstructor != null) {
107101
return resolvedConstructor;
108102
}
109-
Executable resolvedConstructorOrFactoryMethod = getField(beanDefinition,
110-
"resolvedConstructorOrFactoryMethod", Executable.class);
111-
if (resolvedConstructorOrFactoryMethod != null) {
112-
logger.error(
113-
"resolvedConstructorOrFactoryMethod required for " + beanDefinition);
114-
return resolvedConstructorOrFactoryMethod;
103+
104+
Field field = ReflectionUtils.findField(RootBeanDefinition.class, "resolvedConstructorOrFactoryMethod");
105+
if (field != null) {
106+
ReflectionUtils.makeAccessible(field);
107+
return (Executable) ReflectionUtils.getField(field, beanDefinition);
115108
}
109+
116110
return null;
117111
}
118112

@@ -132,15 +126,19 @@ private ResolvableType determineParameterValueType(ValueHolder valueHolder) {
132126
return ResolvableType.forClass(loadClass(valueHolder.getType()));
133127
}
134128
Object value = valueHolder.getValue();
135-
if (value instanceof BeanReference) {
136-
return ResolvableType.forClass(this.beanFactory
137-
.getType(((BeanReference) value).getBeanName(), false));
129+
if (value instanceof BeanReference br) {
130+
if (value instanceof RuntimeBeanReference rbr) {
131+
if (rbr.getBeanType() != null) {
132+
return ResolvableType.forClass(rbr.getBeanType());
133+
}
134+
}
135+
return ResolvableType.forClass(this.beanFactory.getType(br.getBeanName(), false));
138136
}
139-
if (value instanceof BeanDefinition) {
140-
return extractTypeFromBeanDefinition(getBeanType((BeanDefinition) value));
137+
if (value instanceof BeanDefinition bd) {
138+
return extractTypeFromBeanDefinition(getBeanType(bd));
141139
}
142-
if (value instanceof Class<?>) {
143-
return ResolvableType.forClassWithGenerics(Class.class, (Class<?>) value);
140+
if (value instanceof Class<?> clazz) {
141+
return ResolvableType.forClassWithGenerics(Class.class, clazz);
144142
}
145143
return ResolvableType.forInstance(value);
146144
}
@@ -153,9 +151,7 @@ private ResolvableType extractTypeFromBeanDefinition(ResolvableType type) {
153151
}
154152

155153
@Nullable
156-
private Method resolveFactoryMethod(BeanDefinition beanDefinition,
157-
List<ResolvableType> valueTypes) {
158-
154+
private Method resolveFactoryMethod(BeanDefinition beanDefinition, List<ResolvableType> valueTypes) {
159155
if (beanDefinition instanceof RootBeanDefinition rbd) {
160156
Method resolvedFactoryMethod = rbd.getResolvedFactoryMethod();
161157
if (resolvedFactoryMethod != null) {
@@ -165,15 +161,13 @@ private Method resolveFactoryMethod(BeanDefinition beanDefinition,
165161
String factoryMethodName = beanDefinition.getFactoryMethodName();
166162
if (factoryMethodName != null) {
167163
String factoryBeanName = beanDefinition.getFactoryBeanName();
168-
Class<?> beanClass = getBeanClass((factoryBeanName != null)
169-
? this.beanFactory.getMergedBeanDefinition(factoryBeanName)
170-
: beanDefinition);
164+
Class<?> beanClass = getBeanClass(factoryBeanName != null ?
165+
this.beanFactory.getMergedBeanDefinition(factoryBeanName) : beanDefinition);
171166
List<Method> methods = new ArrayList<>();
172167
Assert.state(beanClass != null,
173168
() -> "Failed to determine bean class of " + beanDefinition);
174169
ReflectionUtils.doWithMethods(beanClass, methods::add,
175-
method -> isFactoryMethodCandidate(beanClass, method,
176-
factoryMethodName));
170+
method -> isFactoryMethodCandidate(beanClass, method, factoryMethodName));
177171
if (methods.size() >= 1) {
178172
Function<Method, List<ResolvableType>> parameterTypesFactory = method -> {
179173
List<ResolvableType> types = new ArrayList<>();
@@ -182,16 +176,13 @@ private Method resolveFactoryMethod(BeanDefinition beanDefinition,
182176
}
183177
return types;
184178
};
185-
return (Method) resolveFactoryMethod(methods, parameterTypesFactory,
186-
valueTypes);
179+
return (Method) resolveFactoryMethod(methods, parameterTypesFactory, valueTypes);
187180
}
188181
}
189182
return null;
190183
}
191184

192-
private boolean isFactoryMethodCandidate(Class<?> beanClass, Method method,
193-
String factoryMethodName) {
194-
185+
private boolean isFactoryMethodCandidate(Class<?> beanClass, Method method, String factoryMethodName) {
195186
if (method.getName().equals(factoryMethodName)) {
196187
if (Modifier.isStatic(method.getModifiers())) {
197188
return method.getDeclaringClass().equals(beanClass);
@@ -202,9 +193,7 @@ private boolean isFactoryMethodCandidate(Class<?> beanClass, Method method,
202193
}
203194

204195
@Nullable
205-
private Executable resolveConstructor(Supplier<ResolvableType> beanType,
206-
List<ResolvableType> valueTypes) {
207-
196+
private Executable resolveConstructor(Supplier<ResolvableType> beanType, List<ResolvableType> valueTypes) {
208197
Class<?> type = ClassUtils.getUserClass(beanType.get().toClass());
209198
Constructor<?>[] constructors = type.getDeclaredConstructors();
210199
if (constructors.length == 1) {
@@ -240,47 +229,41 @@ private Executable resolveConstructor(Supplier<ResolvableType> beanType,
240229
List<? extends Executable> typeConversionFallbackMatches = Arrays
241230
.stream(constructors)
242231
.filter(executable -> match(parameterTypesFactory.apply(executable),
243-
valueTypes,
244-
ConstructorOrFactoryMethodResolver.FallbackMode.TYPE_CONVERSION))
232+
valueTypes, FallbackMode.TYPE_CONVERSION))
245233
.toList();
246234
return (typeConversionFallbackMatches.size() == 1)
247235
? typeConversionFallbackMatches.get(0) : null;
248236
}
249237

238+
@Nullable
250239
private Executable resolveFactoryMethod(List<Method> executables,
251240
Function<Method, List<ResolvableType>> parameterTypesFactory,
252241
List<ResolvableType> valueTypes) {
253242

254243
List<? extends Executable> matches = executables.stream()
255-
.filter(executable -> match(parameterTypesFactory.apply(executable),
256-
valueTypes, ConstructorOrFactoryMethodResolver.FallbackMode.NONE))
244+
.filter(executable -> match(parameterTypesFactory.apply(executable), valueTypes, FallbackMode.NONE))
257245
.toList();
258246
if (matches.size() == 1) {
259247
return matches.get(0);
260248
}
261249
List<? extends Executable> assignableElementFallbackMatches = executables.stream()
262250
.filter(executable -> match(parameterTypesFactory.apply(executable),
263-
valueTypes,
264-
ConstructorOrFactoryMethodResolver.FallbackMode.ASSIGNABLE_ELEMENT))
251+
valueTypes, FallbackMode.ASSIGNABLE_ELEMENT))
265252
.toList();
266253
if (assignableElementFallbackMatches.size() == 1) {
267254
return assignableElementFallbackMatches.get(0);
268255
}
269256
List<? extends Executable> typeConversionFallbackMatches = executables.stream()
270257
.filter(executable -> match(parameterTypesFactory.apply(executable),
271-
valueTypes,
272-
ConstructorOrFactoryMethodResolver.FallbackMode.TYPE_CONVERSION))
258+
valueTypes, FallbackMode.TYPE_CONVERSION))
273259
.toList();
274260
Assert.state(typeConversionFallbackMatches.size() <= 1,
275-
() -> "Multiple matches with parameters '" + valueTypes + "': "
276-
+ typeConversionFallbackMatches);
277-
return (typeConversionFallbackMatches.size() == 1)
278-
? typeConversionFallbackMatches.get(0) : null;
261+
() -> "Multiple matches with parameters '" + valueTypes + "': " + typeConversionFallbackMatches);
262+
return (typeConversionFallbackMatches.size() == 1 ? typeConversionFallbackMatches.get(0) : null);
279263
}
280264

281-
private boolean match(List<ResolvableType> parameterTypes,
282-
List<ResolvableType> valueTypes,
283-
ConstructorOrFactoryMethodResolver.FallbackMode fallbackMode) {
265+
private boolean match(
266+
List<ResolvableType> parameterTypes, List<ResolvableType> valueTypes, FallbackMode fallbackMode) {
284267

285268
if (parameterTypes.size() != valueTypes.size()) {
286269
return false;
@@ -293,17 +276,14 @@ private boolean match(List<ResolvableType> parameterTypes,
293276
return true;
294277
}
295278

296-
private boolean isMatch(ResolvableType parameterType, ResolvableType valueType,
297-
ConstructorOrFactoryMethodResolver.FallbackMode fallbackMode) {
298-
279+
private boolean isMatch(ResolvableType parameterType, ResolvableType valueType, FallbackMode fallbackMode) {
299280
if (isAssignable(valueType).test(parameterType)) {
300281
return true;
301282
}
302283
return switch (fallbackMode) {
303-
case ASSIGNABLE_ELEMENT -> isAssignable(valueType)
304-
.test(extractElementType(parameterType));
305-
case TYPE_CONVERSION -> typeConversionFallback(valueType).test(parameterType);
306-
default -> false;
284+
case ASSIGNABLE_ELEMENT -> isAssignable(valueType).test(extractElementType(parameterType));
285+
case TYPE_CONVERSION -> typeConversionFallback(valueType).test(parameterType);
286+
default -> false;
307287
};
308288
}
309289

@@ -323,12 +303,10 @@ private ResolvableType extractElementType(ResolvableType parameterType) {
323303

324304
private Predicate<ResolvableType> typeConversionFallback(ResolvableType valueType) {
325305
return parameterType -> {
326-
if (valueOrCollection(valueType, this::isStringForClassFallback)
327-
.test(parameterType)) {
306+
if (valueOrCollection(valueType, this::isStringForClassFallback).test(parameterType)) {
328307
return true;
329308
}
330-
return valueOrCollection(valueType, this::isSimpleConvertibleType)
331-
.test(parameterType);
309+
return valueOrCollection(valueType, this::isSimpleValueType).test(parameterType);
332310
};
333311
}
334312

@@ -339,12 +317,10 @@ private Predicate<ResolvableType> valueOrCollection(ResolvableType valueType,
339317
if (predicateProvider.apply(valueType).test(parameterType)) {
340318
return true;
341319
}
342-
if (predicateProvider.apply(extractElementType(valueType))
343-
.test(extractElementType(parameterType))) {
320+
if (predicateProvider.apply(extractElementType(valueType)).test(extractElementType(parameterType))) {
344321
return true;
345322
}
346-
return (predicateProvider.apply(valueType)
347-
.test(extractElementType(parameterType)));
323+
return (predicateProvider.apply(valueType).test(extractElementType(parameterType)));
348324
};
349325
}
350326

@@ -358,34 +334,32 @@ private Predicate<ResolvableType> valueOrCollection(ResolvableType valueType,
358334
* parameter
359335
*/
360336
private Predicate<ResolvableType> isStringForClassFallback(ResolvableType valueType) {
361-
return parameterType -> (valueType.isAssignableFrom(String.class)
362-
&& parameterType.isAssignableFrom(Class.class));
337+
return parameterType -> (valueType.isAssignableFrom(String.class) &&
338+
parameterType.isAssignableFrom(Class.class));
363339
}
364340

365-
private Predicate<ResolvableType> isSimpleConvertibleType(ResolvableType valueType) {
366-
return parameterType -> isSimpleConvertibleType(parameterType.toClass())
367-
&& isSimpleConvertibleType(valueType.toClass());
341+
private Predicate<ResolvableType> isSimpleValueType(ResolvableType valueType) {
342+
return parameterType -> (BeanUtils.isSimpleValueType(parameterType.toClass()) &&
343+
BeanUtils.isSimpleValueType(valueType.toClass()));
368344
}
369345

370346
@Nullable
371347
private Class<?> getFactoryBeanClass(BeanDefinition beanDefinition) {
372348
if (beanDefinition instanceof RootBeanDefinition rbd) {
373349
if (rbd.hasBeanClass()) {
374350
Class<?> beanClass = rbd.getBeanClass();
375-
return FactoryBean.class.isAssignableFrom(beanClass) ? beanClass : null;
351+
return (FactoryBean.class.isAssignableFrom(beanClass) ? beanClass : null);
376352
}
377353
}
378354
return null;
379355
}
380356

381357
@Nullable
382358
private Class<?> getBeanClass(BeanDefinition beanDefinition) {
383-
if (beanDefinition instanceof AbstractBeanDefinition abd) {
384-
return abd.hasBeanClass() ? abd.getBeanClass()
385-
: loadClass(abd.getBeanClassName());
359+
if (beanDefinition instanceof AbstractBeanDefinition abd && abd.hasBeanClass()) {
360+
return abd.getBeanClass();
386361
}
387-
return (beanDefinition.getBeanClassName() != null)
388-
? loadClass(beanDefinition.getBeanClassName()) : null;
362+
return (beanDefinition.getBeanClassName() != null ? loadClass(beanDefinition.getBeanClassName()) : null);
389363
}
390364

391365
private ResolvableType getBeanType(BeanDefinition beanDefinition) {
@@ -416,21 +390,6 @@ private Class<?> loadClass(String beanClassName) {
416390
}
417391

418392
@Nullable
419-
private <T> T getField(BeanDefinition beanDefinition, String fieldName,
420-
Class<T> targetType) {
421-
422-
Field field = ReflectionUtils.findField(RootBeanDefinition.class, fieldName);
423-
ReflectionUtils.makeAccessible(field);
424-
return targetType.cast(ReflectionUtils.getField(field, beanDefinition));
425-
}
426-
427-
private static boolean isSimpleConvertibleType(Class<?> type) {
428-
return (type.isPrimitive() && type != void.class) || type == Double.class
429-
|| type == Float.class || type == Long.class || type == Integer.class
430-
|| type == Short.class || type == Character.class || type == Byte.class
431-
|| type == Boolean.class || type == String.class;
432-
}
433-
434393
static Executable resolve(RegisteredBean registeredBean) {
435394
return new ConstructorOrFactoryMethodResolver(registeredBean.getBeanFactory())
436395
.resolve(registeredBean.getMergedBeanDefinition());
@@ -444,7 +403,6 @@ enum FallbackMode {
444403
ASSIGNABLE_ELEMENT,
445404

446405
TYPE_CONVERSION
447-
448406
}
449407

450408
}

0 commit comments

Comments
 (0)