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;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -31,8 +30,6 @@ import org.springframework.beans.BeanInstantiationException;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.TypeConverter;
|
||||
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.config.ConfigurableBeanFactory;
|
||||
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.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -239,7 +235,7 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
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());
|
||||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
|
|
@ -264,8 +260,8 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
dependencyDescriptor, shortcut, beanClass);
|
||||
}
|
||||
ValueHolder argumentValue = argumentValues.getIndexedArgumentValue(i, null);
|
||||
resolved[i - startIndex] = resolveArgument(beanFactory, beanName,
|
||||
autowiredBeans, parameter, dependencyDescriptor, argumentValue);
|
||||
resolved[i - startIndex] = resolveArgument(registeredBean,autowiredBeans,
|
||||
dependencyDescriptor, argumentValue);
|
||||
}
|
||||
registerDependentBeans(beanFactory, beanName, autowiredBeans);
|
||||
return AutowiredArguments.of(resolved);
|
||||
|
|
@ -311,36 +307,21 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveArgument(AbstractAutowireCapableBeanFactory beanFactory,
|
||||
String beanName, Set<String> autowiredBeans, MethodParameter parameter,
|
||||
private Object resolveArgument(RegisteredBean registeredBean, Set<String> autowiredBeans,
|
||||
DependencyDescriptor dependencyDescriptor, @Nullable ValueHolder argumentValue) {
|
||||
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
Class<?> parameterType = parameter.getParameterType();
|
||||
TypeConverter typeConverter = registeredBean.getBeanFactory().getTypeConverter();
|
||||
Class<?> parameterType = dependencyDescriptor.getMethodParameter().getParameterType();
|
||||
if (argumentValue != null) {
|
||||
return (!argumentValue.isConverted()) ?
|
||||
typeConverter.convertIfNecessary(argumentValue.getValue(), parameterType) :
|
||||
argumentValue.getConvertedValue();
|
||||
}
|
||||
try {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
return registeredBean.resolveAutowiredArgument(dependencyDescriptor, typeConverter, autowiredBeans);
|
||||
}
|
||||
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?");
|
||||
}
|
||||
try {
|
||||
Object autowiredArgument = resolveAutowiredArgument(
|
||||
methodParam, beanName, autowiredBeanNames, converter, fallback);
|
||||
Object autowiredArgument = resolveAutowiredArgument(new DependencyDescriptor(methodParam, true),
|
||||
beanName, autowiredBeanNames, converter, fallback);
|
||||
args.rawArguments[paramIndex] = autowiredArgument;
|
||||
args.arguments[paramIndex] = autowiredArgument;
|
||||
args.preparedArguments[paramIndex] = autowiredArgumentMarker;
|
||||
|
|
@ -831,7 +831,8 @@ class ConstructorResolver {
|
|||
Object argValue = argsToResolve[argIndex];
|
||||
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
|
||||
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) {
|
||||
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.
|
||||
*/
|
||||
@Nullable
|
||||
protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
|
||||
protected Object resolveAutowiredArgument(DependencyDescriptor descriptor, String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
|
||||
|
||||
Class<?> paramType = param.getParameterType();
|
||||
Class<?> paramType = descriptor.getMethodParameter().getParameterType();
|
||||
if (InjectionPoint.class.isAssignableFrom(paramType)) {
|
||||
InjectionPoint injectionPoint = currentInjectionPoint.get();
|
||||
if (injectionPoint == null) {
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + param);
|
||||
throw new IllegalStateException("No current InjectionPoint available for " + descriptor);
|
||||
}
|
||||
return injectionPoint;
|
||||
}
|
||||
try {
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
catch (NoUniqueBeanDefinitionException ex) {
|
||||
throw ex;
|
||||
|
|
|
|||
|
|
@ -17,13 +17,16 @@
|
|||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.beans.TypeConverter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -209,6 +212,13 @@ public final class RegisteredBean {
|
|||
.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
|
||||
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.GenericTemplateConfiguration;
|
||||
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.LazyAutowiredMethodComponent;
|
||||
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
|
||||
@CompileWithForkedClassLoader
|
||||
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