diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java index 1c295379f20f..67f7f4204736 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java @@ -21,7 +21,6 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Parameter; -import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.function.Consumer; @@ -41,6 +40,7 @@ import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.support.ClassHintUtils; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.AutowireCandidateResolver; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -439,8 +439,8 @@ public void registerRuntimeHints(RuntimeHints runtimeHints, Executable executabl private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) { Class proxyType = this.candidateResolver.getLazyResolutionProxyClass(dependencyDescriptor, null); - if (proxyType != null && Proxy.isProxyClass(proxyType)) { - runtimeHints.proxies().registerJdkProxy(proxyType.getInterfaces()); + if (proxyType != null) { + ClassHintUtils.registerProxyIfNecessary(proxyType, runtimeHints); } } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java index b440cff9f13c..99582de7e333 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java @@ -16,6 +16,9 @@ package org.springframework.beans.factory.aot; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; @@ -29,10 +32,14 @@ import org.springframework.aot.generate.GeneratedClass; import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.TypeHint; +import org.springframework.aot.hint.TypeReference; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.DependencyDescriptor; +import org.springframework.beans.factory.support.AutowireCandidateResolver; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.InstanceSupplier; @@ -61,6 +68,8 @@ import org.springframework.beans.testfixture.beans.factory.generator.factory.NumberHolderFactoryBean; import org.springframework.beans.testfixture.beans.factory.generator.factory.SampleFactory; import org.springframework.beans.testfixture.beans.factory.generator.injection.InjectionComponent; +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.NoOp; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.test.tools.Compiled; import org.springframework.core.test.tools.TestCompiler; @@ -114,6 +123,30 @@ void generateWhenHasConstructorWithParameter() { assertThat(getReflectionHints().getTypeHint(InjectionComponent.class)).isNotNull(); } + @Test + void generateWhenHasConstructorWithLazyInterfaceParameterRegistersJdkProxyHint() { + this.beanFactory.setAutowireCandidateResolver(new TestLazyAutowireCandidateResolver()); + BeanDefinition beanDefinition = new RootBeanDefinition(LazyInterfaceConstructorComponent.class); + generate(beanDefinition); + assertThat(this.generationContext.getRuntimeHints().proxies().jdkProxyHints()).singleElement().satisfies( + hint -> assertThat(hint.getProxiedInterfaces()) + .containsExactly(TypeReference.of(LazyInterfaceDependency.class))); + } + + @Test + void generateWhenHasConstructorWithLazyClassParameterRegistersCglibProxyHints() { + TestLazyAutowireCandidateResolver resolver = new TestLazyAutowireCandidateResolver(); + this.beanFactory.setAutowireCandidateResolver(resolver); + BeanDefinition beanDefinition = new RootBeanDefinition(LazyClassConstructorComponent.class); + generate(beanDefinition); + assertThat(getReflectionHints().getTypeHint(resolver.getProxyClass(LazyClassDependency.class))) + .satisfies(hasMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS)); + assertThat(getReflectionHints().getTypeHint(LazyClassDependency.class)) + .satisfies(hasMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS, + MemberCategory.INVOKE_DECLARED_METHODS)); + } + @Test void generateWhenHasConstructorWithInnerClassAndDefaultConstructor() { RootBeanDefinition beanDefinition = new RootBeanDefinition(NoDependencyComponent.class); @@ -217,6 +250,34 @@ void generateWhenHasFactoryMethodWithNoArg() { assertThat(getReflectionHints().getTypeHint(SimpleConfiguration.class)).isNotNull(); } + @Test + void generateWhenHasFactoryMethodWithLazyInterfaceParameterRegistersJdkProxyHint() { + this.beanFactory.setAutowireCandidateResolver(new TestLazyAutowireCandidateResolver()); + BeanDefinition beanDefinition = BeanDefinitionBuilder + .rootBeanDefinition(LazyFactory.class) + .setFactoryMethod("interfaceComponent").getBeanDefinition(); + generate(beanDefinition); + assertThat(this.generationContext.getRuntimeHints().proxies().jdkProxyHints()).singleElement().satisfies( + hint -> assertThat(hint.getProxiedInterfaces()) + .containsExactly(TypeReference.of(LazyInterfaceDependency.class))); + } + + @Test + void generateWhenHasFactoryMethodWithLazyClassParameterRegistersCglibProxyHints() { + TestLazyAutowireCandidateResolver resolver = new TestLazyAutowireCandidateResolver(); + this.beanFactory.setAutowireCandidateResolver(resolver); + BeanDefinition beanDefinition = BeanDefinitionBuilder + .rootBeanDefinition(LazyFactory.class) + .setFactoryMethod("classComponent").getBeanDefinition(); + generate(beanDefinition); + assertThat(getReflectionHints().getTypeHint(resolver.getProxyClass(LazyClassDependency.class))) + .satisfies(hasMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS)); + assertThat(getReflectionHints().getTypeHint(LazyClassDependency.class)) + .satisfies(hasMemberCategories(MemberCategory.INVOKE_PUBLIC_METHODS, + MemberCategory.INVOKE_DECLARED_METHODS)); + } + @Test void generateWhenHasFactoryMethodOnInterface() { BeanDefinition beanDefinition = BeanDefinitionBuilder @@ -316,6 +377,10 @@ private ThrowingConsumer hasMethodWithMode(ExecutableMode mode) { return hint -> assertThat(hint.methods()).anySatisfy(hasMode(mode)); } + private ThrowingConsumer hasMemberCategories(MemberCategory... memberCategories) { + return hint -> assertThat(hint.getMemberCategories()).contains(memberCategories); + } + private ThrowingConsumer hasMode(ExecutableMode mode) { return hint -> assertThat(hint.getMode()).isEqualTo(mode); } @@ -331,6 +396,19 @@ private void compile(BeanDefinition beanDefinition, BiConsumer, Compiled> result) { @@ -359,6 +437,80 @@ private void compile(TestCompiler testCompiler, BeanDefinition beanDefinition, } + private static class TestLazyAutowireCandidateResolver implements AutowireCandidateResolver { + + private final Map, Class> proxyClasses = new HashMap<>(); + + @Override + public Class getLazyResolutionProxyClass(DependencyDescriptor descriptor, String beanName) { + Class dependencyType = descriptor.getDependencyType(); + if (dependencyType.isInterface()) { + return Proxy.newProxyInstance(dependencyType.getClassLoader(), new Class[] {dependencyType}, + (proxy, method, args) -> null).getClass(); + } + return getProxyClass(dependencyType); + } + + Class getProxyClass(Class type) { + return this.proxyClasses.computeIfAbsent(type, this::createProxyClass); + } + + private Class createProxyClass(Class type) { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(type); + enhancer.setCallbackType(NoOp.class); + return enhancer.createClass(); + } + + @Override + public AutowireCandidateResolver cloneIfNecessary() { + return this; + } + } + + + interface LazyInterfaceDependency { + } + + + static class LazyClassDependency { + } + + + static class LazyInterfaceConstructorComponent { + + LazyInterfaceConstructorComponent(LazyInterfaceDependency dependency) { + } + } + + + static class LazyClassConstructorComponent { + + LazyClassConstructorComponent(LazyClassDependency dependency) { + } + } + + + static class LazyInterfaceFactoryMethodComponent { + } + + + static class LazyClassFactoryMethodComponent { + } + + + static class LazyFactory { + + static LazyInterfaceFactoryMethodComponent interfaceComponent(LazyInterfaceDependency dependency) { + return new LazyInterfaceFactoryMethodComponent(); + } + + static LazyClassFactoryMethodComponent classComponent(LazyClassDependency dependency) { + return new LazyClassFactoryMethodComponent(); + } + } + + @Nested @SuppressWarnings("deprecation") class DeprecationTests {