Infer proxy on @Lazy-annotated injection points

This commit makes use of the new `getLazyResolutionProxyClass` on
`AutowireCandidateResolver` to detect if a injection point requires
a proxy.

Closes gh-28980
This commit is contained in:
Stephane Nicoll
2022-08-22 19:59:21 +02:00
parent e5f9bb76b1
commit 455715899d
7 changed files with 387 additions and 4 deletions

View File

@@ -17,12 +17,15 @@
package org.springframework.context.aot;
import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GeneratedFiles.Kind;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generator.compile.Compiled;
@@ -44,11 +47,18 @@ import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.testfixture.context.generator.SimpleComponent;
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration;
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredFieldComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredMethodComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyConstructorArgumentComponent;
import org.springframework.context.testfixture.context.generator.annotation.LazyFactoryMethodArgumentComponent;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.testfixture.aot.generate.TestGenerationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -93,6 +103,87 @@ class ApplicationContextAotGeneratorTests {
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnField() {
testAutowiredComponent(LazyAutowiredFieldComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnMethod() {
testAutowiredComponent(LazyAutowiredMethodComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isNotInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
resourceLoader.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnConstructor() {
testAutowiredComponent(LazyConstructorArgumentComponent.class, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
@Test
void processAheadOfTimeWhenHasLazyAutowiringOnFactoryMethod() {
RootBeanDefinition bd = new RootBeanDefinition(LazyFactoryMethodArgumentComponent.class);
bd.setFactoryMethodName("of");
testAutowiredComponent(LazyFactoryMethodArgumentComponent.class, bd, (bean, generationContext) -> {
Environment environment = bean.getEnvironment();
assertThat(environment).isInstanceOf(Proxy.class);
ResourceLoader resourceLoader = bean.getResourceLoader();
assertThat(resourceLoader).isNotInstanceOf(Proxy.class);
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
assertThat(runtimeHints.proxies().jdkProxies()).singleElement().satisfies(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
private <T> void testAutowiredComponent(Class<T> type, BiConsumer<T, GenerationContext> assertions) {
testAutowiredComponent(type, new RootBeanDefinition(type), assertions);
}
private <T> void testAutowiredComponent(Class<T> type, RootBeanDefinition beanDefinition,
BiConsumer<T, GenerationContext> assertions) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.getDefaultListableBeanFactory().setAutowireCandidateResolver(
new ContextAnnotationAutowireCandidateResolver());
applicationContext.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
applicationContext.registerBeanDefinition("testComponent", beanDefinition);
TestGenerationContext generationContext = processAheadOfTime(applicationContext);
testCompiledResult(generationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("testComponent");
assertions.accept(freshApplicationContext.getBean("testComponent", type), generationContext);
});
}
@Test
void processAheadOfTimeWhenHasInitDestroyMethods() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
@@ -189,10 +280,14 @@ class ApplicationContextAotGeneratorTests {
return generationContext;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void testCompiledResult(GenericApplicationContext applicationContext,
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
TestGenerationContext generationContext = processAheadOfTime(applicationContext);
testCompiledResult(processAheadOfTime(applicationContext), result);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void testCompiledResult(TestGenerationContext generationContext,
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
TestCompiler.forSystem().withFiles(generationContext.getGeneratedFiles()).compile(compiled ->
result.accept(compiled.getInstance(ApplicationContextInitializer.class), compiled));
}