Skip to content

Commit c1f6d71

Browse files
committed
Add support for InjectionPoint with AOT
This commit reviews BeanInstanceSupplier to reuse more code from ConstructorResolver. Previously, the autowired argument resolution was partially duplicated and this commit introduces a new common path via RegisteredBean#resolveAutowiredArgument. Closes gh-30401
1 parent a133aae commit c1f6d71

File tree

5 files changed

+82
-34
lines changed

5 files changed

+82
-34
lines changed

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

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.beans.factory.aot;
1818

19-
import java.lang.reflect.Array;
2019
import java.lang.reflect.Constructor;
2120
import java.lang.reflect.Executable;
2221
import java.lang.reflect.Method;
@@ -31,8 +30,6 @@
3130
import org.springframework.beans.BeansException;
3231
import org.springframework.beans.TypeConverter;
3332
import org.springframework.beans.factory.BeanFactory;
34-
import org.springframework.beans.factory.InjectionPoint;
35-
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3633
import org.springframework.beans.factory.UnsatisfiedDependencyException;
3734
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
3835
import org.springframework.beans.factory.config.ConstructorArgumentValues;
@@ -44,7 +41,6 @@
4441
import org.springframework.beans.factory.support.RegisteredBean;
4542
import org.springframework.beans.factory.support.RootBeanDefinition;
4643
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
47-
import org.springframework.core.CollectionFactory;
4844
import org.springframework.core.MethodParameter;
4945
import org.springframework.lang.Nullable;
5046
import org.springframework.util.Assert;
@@ -239,7 +235,7 @@ AutowiredArguments resolveArguments(RegisteredBean registeredBean) {
239235
return resolveArguments(registeredBean, this.lookup.get(registeredBean));
240236
}
241237

242-
private AutowiredArguments resolveArguments(RegisteredBean registeredBean,Executable executable) {
238+
private AutowiredArguments resolveArguments(RegisteredBean registeredBean, Executable executable) {
243239
Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, registeredBean.getBeanFactory());
244240
String beanName = registeredBean.getBeanName();
245241
Class<?> beanClass = registeredBean.getBeanClass();
@@ -264,8 +260,8 @@ private AutowiredArguments resolveArguments(RegisteredBean registeredBean,Execut
264260
dependencyDescriptor, shortcut, beanClass);
265261
}
266262
ValueHolder argumentValue = argumentValues.getIndexedArgumentValue(i, null);
267-
resolved[i - startIndex] = resolveArgument(beanFactory, beanName,
268-
autowiredBeans, parameter, dependencyDescriptor, argumentValue);
263+
resolved[i - startIndex] = resolveArgument(registeredBean,autowiredBeans,
264+
dependencyDescriptor, argumentValue);
269265
}
270266
registerDependentBeans(beanFactory, beanName, autowiredBeans);
271267
return AutowiredArguments.of(resolved);
@@ -311,36 +307,21 @@ private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver, V
311307
}
312308

313309
@Nullable
314-
private Object resolveArgument(AbstractAutowireCapableBeanFactory beanFactory,
315-
String beanName, Set<String> autowiredBeans, MethodParameter parameter,
310+
private Object resolveArgument(RegisteredBean registeredBean, Set<String> autowiredBeans,
316311
DependencyDescriptor dependencyDescriptor, @Nullable ValueHolder argumentValue) {
317312

318-
TypeConverter typeConverter = beanFactory.getTypeConverter();
319-
Class<?> parameterType = parameter.getParameterType();
313+
TypeConverter typeConverter = registeredBean.getBeanFactory().getTypeConverter();
314+
Class<?> parameterType = dependencyDescriptor.getMethodParameter().getParameterType();
320315
if (argumentValue != null) {
321316
return (!argumentValue.isConverted()) ?
322317
typeConverter.convertIfNecessary(argumentValue.getValue(), parameterType) :
323318
argumentValue.getConvertedValue();
324319
}
325320
try {
326-
try {
327-
return beanFactory.resolveDependency(dependencyDescriptor, beanName, autowiredBeans, typeConverter);
328-
}
329-
catch (NoSuchBeanDefinitionException ex) {
330-
if (parameterType.isArray()) {
331-
return Array.newInstance(parameterType.getComponentType(), 0);
332-
}
333-
if (CollectionFactory.isApproximableCollectionType(parameterType)) {
334-
return CollectionFactory.createCollection(parameterType, 0);
335-
}
336-
if (CollectionFactory.isApproximableMapType(parameterType)) {
337-
return CollectionFactory.createMap(parameterType, 0);
338-
}
339-
throw ex;
340-
}
321+
return registeredBean.resolveAutowiredArgument(dependencyDescriptor, typeConverter, autowiredBeans);
341322
}
342323
catch (BeansException ex) {
343-
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex);
324+
throw new UnsatisfiedDependencyException(null, registeredBean.getBeanName(), dependencyDescriptor, ex);
344325
}
345326
}
346327

spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -788,8 +788,8 @@ private ArgumentsHolder createArgumentArray(
788788
"] - did you specify the correct bean references as arguments?");
789789
}
790790
try {
791-
Object autowiredArgument = resolveAutowiredArgument(
792-
methodParam, beanName, autowiredBeanNames, converter, fallback);
791+
Object autowiredArgument = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
792+
beanName, autowiredBeanNames, converter, fallback);
793793
args.rawArguments[paramIndex] = autowiredArgument;
794794
args.arguments[paramIndex] = autowiredArgument;
795795
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
@@ -831,7 +831,8 @@ private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mb
831831
Object argValue = argsToResolve[argIndex];
832832
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
833833
if (argValue == autowiredArgumentMarker) {
834-
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
834+
argValue = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
835+
beanName, null, converter, true);
835836
}
836837
else if (argValue instanceof BeanMetadataElement) {
837838
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
@@ -872,20 +873,20 @@ protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor)
872873
* Template method for resolving the specified argument which is supposed to be autowired.
873874
*/
874875
@Nullable
875-
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
876+
protected Object resolveAutowiredArgument(DependencyDescriptor descriptor, String beanName,
876877
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
877878

878-
Class<?> paramType = param.getParameterType();
879+
Class<?> paramType = descriptor.getMethodParameter().getParameterType();
879880
if (InjectionPoint.class.isAssignableFrom(paramType)) {
880881
InjectionPoint injectionPoint = currentInjectionPoint.get();
881882
if (injectionPoint == null) {
882-
throw new IllegalStateException("No current InjectionPoint available for " + param);
883+
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
883884
}
884885
return injectionPoint;
885886
}
886887
try {
887888
return this.beanFactory.resolveDependency(
888-
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
889+
descriptor, beanName, autowiredBeanNames, typeConverter);
889890
}
890891
catch (NoUniqueBeanDefinitionException ex) {
891892
throw ex;

spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
package org.springframework.beans.factory.support;
1818

1919
import java.lang.reflect.Executable;
20+
import java.util.Set;
2021
import java.util.function.BiFunction;
2122
import java.util.function.Supplier;
2223

24+
import org.springframework.beans.TypeConverter;
2325
import org.springframework.beans.factory.BeanFactory;
2426
import org.springframework.beans.factory.config.BeanDefinition;
2527
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2628
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
29+
import org.springframework.beans.factory.config.DependencyDescriptor;
2730
import org.springframework.core.ResolvableType;
2831
import org.springframework.core.style.ToStringCreator;
2932
import org.springframework.lang.Nullable;
@@ -209,6 +212,13 @@ public Executable resolveConstructorOrFactoryMethod() {
209212
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
210213
}
211214

215+
@Nullable
216+
public Object resolveAutowiredArgument(DependencyDescriptor descriptor, TypeConverter typeConverter,
217+
Set<String> autowiredBeans) {
218+
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
219+
.resolveAutowiredArgument(descriptor, getBeanName(), autowiredBeans, typeConverter, true);
220+
}
221+
212222

213223
@Override
214224
public String toString() {

spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.context.testfixture.context.annotation.ConfigurableCglibConfiguration;
6060
import org.springframework.context.testfixture.context.annotation.GenericTemplateConfiguration;
6161
import org.springframework.context.testfixture.context.annotation.InitDestroyComponent;
62+
import org.springframework.context.testfixture.context.annotation.InjectionPointConfiguration;
6263
import org.springframework.context.testfixture.context.annotation.LazyAutowiredFieldComponent;
6364
import org.springframework.context.testfixture.context.annotation.LazyAutowiredMethodComponent;
6465
import org.springframework.context.testfixture.context.annotation.LazyConstructorArgumentComponent;
@@ -315,6 +316,17 @@ void processAheadOfTimeWithQualifier() {
315316
});
316317
}
317318

319+
@Test
320+
void processAheadOfTimeWithInjectionPoint() {
321+
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
322+
applicationContext.registerBean(InjectionPointConfiguration.class);
323+
testCompiledResult(applicationContext, (initializer, compiled) -> {
324+
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
325+
assertThat(freshApplicationContext.getBean("classToString"))
326+
.isEqualTo(InjectionPointConfiguration.class.getName());
327+
});
328+
}
329+
318330
@Nested
319331
@CompileWithForkedClassLoader
320332
class ConfigurationClassCglibProxy {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.testfixture.context.annotation;
18+
19+
import org.springframework.beans.factory.InjectionPoint;
20+
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.context.annotation.Scope;
24+
25+
@Configuration(proxyBeanMethods = false)
26+
public class InjectionPointConfiguration {
27+
28+
@Bean
29+
public String classToString(Class<?> callingClass) {
30+
return callingClass.getName();
31+
}
32+
33+
34+
@Configuration(proxyBeanMethods = false)
35+
public static class BeansConfiguration {
36+
37+
@Bean
38+
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
39+
public Class<?> callingClass(InjectionPoint injectionPoint) {
40+
return injectionPoint.getMember().getDeclaringClass();
41+
}
42+
}
43+
44+
}

0 commit comments

Comments
 (0)