diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java index a160d986f897..d408a7b6fdb7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanInstanceSupplier.java @@ -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.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.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 @@ AutowiredArguments resolveArguments(RegisteredBean 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()); String beanName = registeredBean.getBeanName(); Class beanClass = registeredBean.getBeanClass(); @@ -264,8 +260,8 @@ private AutowiredArguments resolveArguments(RegisteredBean registeredBean,Execut 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 @@ private ValueHolder resolveArgumentValue(BeanDefinitionValueResolver resolver, V } @Nullable - private Object resolveArgument(AbstractAutowireCapableBeanFactory beanFactory, - String beanName, Set autowiredBeans, MethodParameter parameter, + private Object resolveArgument(RegisteredBean registeredBean, Set 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); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 8c378959594b..878e6c2cb463 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -788,8 +788,8 @@ private ArgumentsHolder createArgumentArray( "] - 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 @@ private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mb 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 @@ protected Constructor getUserDeclaredConstructor(Constructor constructor) * 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 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; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java index c8c5487153ce..96ab85217671 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RegisteredBean.java @@ -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 Executable resolveConstructorOrFactoryMethod() { .resolveConstructorOrFactoryMethod(getBeanName(), getMergedBeanDefinition()); } + @Nullable + public Object resolveAutowiredArgument(DependencyDescriptor descriptor, TypeConverter typeConverter, + Set autowiredBeans) { + return new ConstructorResolver((AbstractAutowireCapableBeanFactory) getBeanFactory()) + .resolveAutowiredArgument(descriptor, getBeanName(), autowiredBeans, typeConverter, true); + } + @Override public String toString() { diff --git a/spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java b/spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java index e3b0df915384..01a210cd0d58 100644 --- a/spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java @@ -59,6 +59,7 @@ 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 @@ void processAheadOfTimeWithQualifier() { }); } + @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 { diff --git a/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/InjectionPointConfiguration.java b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/InjectionPointConfiguration.java new file mode 100644 index 000000000000..bb7910b46905 --- /dev/null +++ b/spring-context/src/testFixtures/java/org/springframework/context/testfixture/context/annotation/InjectionPointConfiguration.java @@ -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(); + } + } + +}