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
This commit is contained in:
parent
a133aae8d6
commit
c1f6d7197b
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.aot;
|
package org.springframework.beans.factory.aot;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
@ -31,8 +30,6 @@ import org.springframework.beans.BeanInstantiationException;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.TypeConverter;
|
import org.springframework.beans.TypeConverter;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.InjectionPoint;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||||
|
|
@ -44,7 +41,6 @@ import org.springframework.beans.factory.support.InstanceSupplier;
|
||||||
import org.springframework.beans.factory.support.RegisteredBean;
|
import org.springframework.beans.factory.support.RegisteredBean;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
||||||
import org.springframework.core.CollectionFactory;
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
@ -239,7 +235,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
||||||
return resolveArguments(registeredBean, this.lookup.get(registeredBean));
|
return resolveArguments(registeredBean, this.lookup.get(registeredBean));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AutowiredArguments resolveArguments(RegisteredBean registeredBean,Executable executable) {
|
private AutowiredArguments resolveArguments(RegisteredBean registeredBean, Executable executable) {
|
||||||
Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, registeredBean.getBeanFactory());
|
Assert.isInstanceOf(AbstractAutowireCapableBeanFactory.class, registeredBean.getBeanFactory());
|
||||||
String beanName = registeredBean.getBeanName();
|
String beanName = registeredBean.getBeanName();
|
||||||
Class<?> beanClass = registeredBean.getBeanClass();
|
Class<?> beanClass = registeredBean.getBeanClass();
|
||||||
|
|
@ -264,8 +260,8 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
||||||
dependencyDescriptor, shortcut, beanClass);
|
dependencyDescriptor, shortcut, beanClass);
|
||||||
}
|
}
|
||||||
ValueHolder argumentValue = argumentValues.getIndexedArgumentValue(i, null);
|
ValueHolder argumentValue = argumentValues.getIndexedArgumentValue(i, null);
|
||||||
resolved[i - startIndex] = resolveArgument(beanFactory, beanName,
|
resolved[i - startIndex] = resolveArgument(registeredBean,autowiredBeans,
|
||||||
autowiredBeans, parameter, dependencyDescriptor, argumentValue);
|
dependencyDescriptor, argumentValue);
|
||||||
}
|
}
|
||||||
registerDependentBeans(beanFactory, beanName, autowiredBeans);
|
registerDependentBeans(beanFactory, beanName, autowiredBeans);
|
||||||
return AutowiredArguments.of(resolved);
|
return AutowiredArguments.of(resolved);
|
||||||
|
|
@ -311,36 +307,21 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Object resolveArgument(AbstractAutowireCapableBeanFactory beanFactory,
|
private Object resolveArgument(RegisteredBean registeredBean, Set<String> autowiredBeans,
|
||||||
String beanName, Set<String> autowiredBeans, MethodParameter parameter,
|
|
||||||
DependencyDescriptor dependencyDescriptor, @Nullable ValueHolder argumentValue) {
|
DependencyDescriptor dependencyDescriptor, @Nullable ValueHolder argumentValue) {
|
||||||
|
|
||||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
TypeConverter typeConverter = registeredBean.getBeanFactory().getTypeConverter();
|
||||||
Class<?> parameterType = parameter.getParameterType();
|
Class<?> parameterType = dependencyDescriptor.getMethodParameter().getParameterType();
|
||||||
if (argumentValue != null) {
|
if (argumentValue != null) {
|
||||||
return (!argumentValue.isConverted()) ?
|
return (!argumentValue.isConverted()) ?
|
||||||
typeConverter.convertIfNecessary(argumentValue.getValue(), parameterType) :
|
typeConverter.convertIfNecessary(argumentValue.getValue(), parameterType) :
|
||||||
argumentValue.getConvertedValue();
|
argumentValue.getConvertedValue();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
try {
|
return registeredBean.resolveAutowiredArgument(dependencyDescriptor, typeConverter, autowiredBeans);
|
||||||
return beanFactory.resolveDependency(dependencyDescriptor, beanName, autowiredBeans, typeConverter);
|
|
||||||
}
|
|
||||||
catch (NoSuchBeanDefinitionException ex) {
|
|
||||||
if (parameterType.isArray()) {
|
|
||||||
return Array.newInstance(parameterType.getComponentType(), 0);
|
|
||||||
}
|
|
||||||
if (CollectionFactory.isApproximableCollectionType(parameterType)) {
|
|
||||||
return CollectionFactory.createCollection(parameterType, 0);
|
|
||||||
}
|
|
||||||
if (CollectionFactory.isApproximableMapType(parameterType)) {
|
|
||||||
return CollectionFactory.createMap(parameterType, 0);
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (BeansException ex) {
|
catch (BeansException ex) {
|
||||||
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(parameter), ex);
|
throw new UnsatisfiedDependencyException(null, registeredBean.getBeanName(), dependencyDescriptor, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -788,8 +788,8 @@ 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(
|
Object autowiredArgument = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
|
||||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
beanName, autowiredBeanNames, converter, fallback);
|
||||||
args.rawArguments[paramIndex] = autowiredArgument;
|
args.rawArguments[paramIndex] = autowiredArgument;
|
||||||
args.arguments[paramIndex] = autowiredArgument;
|
args.arguments[paramIndex] = autowiredArgument;
|
||||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
||||||
|
|
@ -831,7 +831,8 @@ class ConstructorResolver {
|
||||||
Object argValue = argsToResolve[argIndex];
|
Object argValue = argsToResolve[argIndex];
|
||||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||||
if (argValue == autowiredArgumentMarker) {
|
if (argValue == autowiredArgumentMarker) {
|
||||||
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
|
argValue = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
|
||||||
|
beanName, null, converter, true);
|
||||||
}
|
}
|
||||||
else if (argValue instanceof BeanMetadataElement) {
|
else if (argValue instanceof BeanMetadataElement) {
|
||||||
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
|
||||||
|
|
@ -872,20 +873,20 @@ class ConstructorResolver {
|
||||||
* Template method for resolving the specified argument which is supposed to be autowired.
|
* Template method for resolving the specified argument which is supposed to be autowired.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
protected Object resolveAutowiredArgument(DependencyDescriptor descriptor, String beanName,
|
||||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||||
|
|
||||||
Class<?> paramType = param.getParameterType();
|
Class<?> paramType = descriptor.getMethodParameter().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(
|
||||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||||
}
|
}
|
||||||
catch (NoUniqueBeanDefinitionException ex) {
|
catch (NoUniqueBeanDefinitionException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,16 @@
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.beans.TypeConverter;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
@ -209,6 +212,13 @@ public final class RegisteredBean {
|
||||||
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
|
.resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Object resolveAutowiredArgument(DependencyDescriptor descriptor, TypeConverter typeConverter,
|
||||||
|
Set<String> autowiredBeans) {
|
||||||
|
return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory())
|
||||||
|
.resolveAutowiredArgument(descriptor, getBeanName(), autowiredBeans, typeConverter, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ import org.springframework.context.testfixture.context.annotation.CglibConfigura
|
||||||
import org.springframework.context.testfixture.context.annotation.ConfigurableCglibConfiguration;
|
import org.springframework.context.testfixture.context.annotation.ConfigurableCglibConfiguration;
|
||||||
import org.springframework.context.testfixture.context.annotation.GenericTemplateConfiguration;
|
import org.springframework.context.testfixture.context.annotation.GenericTemplateConfiguration;
|
||||||
import org.springframework.context.testfixture.context.annotation.InitDestroyComponent;
|
import org.springframework.context.testfixture.context.annotation.InitDestroyComponent;
|
||||||
|
import org.springframework.context.testfixture.context.annotation.InjectionPointConfiguration;
|
||||||
import org.springframework.context.testfixture.context.annotation.LazyAutowiredFieldComponent;
|
import org.springframework.context.testfixture.context.annotation.LazyAutowiredFieldComponent;
|
||||||
import org.springframework.context.testfixture.context.annotation.LazyAutowiredMethodComponent;
|
import org.springframework.context.testfixture.context.annotation.LazyAutowiredMethodComponent;
|
||||||
import org.springframework.context.testfixture.context.annotation.LazyConstructorArgumentComponent;
|
import org.springframework.context.testfixture.context.annotation.LazyConstructorArgumentComponent;
|
||||||
|
|
@ -315,6 +316,17 @@ class ApplicationContextAotGeneratorTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processAheadOfTimeWithInjectionPoint() {
|
||||||
|
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||||
|
applicationContext.registerBean(InjectionPointConfiguration.class);
|
||||||
|
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||||
|
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||||
|
assertThat(freshApplicationContext.getBean("classToString"))
|
||||||
|
.isEqualTo(InjectionPointConfiguration.class.getName());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
@CompileWithForkedClassLoader
|
@CompileWithForkedClassLoader
|
||||||
class ConfigurationClassCglibProxy {
|
class ConfigurationClassCglibProxy {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.context.testfixture.context.annotation;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InjectionPoint;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Scope;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public class InjectionPointConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public String classToString(Class<?> callingClass) {
|
||||||
|
return callingClass.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
public static class BeansConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||||
|
public Class<?> callingClass(InjectionPoint injectionPoint) {
|
||||||
|
return injectionPoint.getMember().getDeclaringClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue