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));
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2022 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.generator.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyAutowiredFieldComponent {
@Lazy
@Autowired
private Environment environment;
@Autowired
private ResourceLoader resourceLoader;
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2022 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.generator.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyAutowiredMethodComponent {
private Environment environment;
private ResourceLoader resourceLoader;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Environment getEnvironment() {
return this.environment;
}
@Autowired
public void setResourceLoader(@Lazy ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2022 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.generator.annotation;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
*
* @author Stephane Nicoll
*/
public class LazyConstructorArgumentComponent {
private final Environment environment;
private final ResourceLoader resourceLoader;
public LazyConstructorArgumentComponent(@Lazy Environment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = resourceLoader;
}
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2022 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.generator.annotation;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
public class LazyFactoryMethodArgumentComponent {
private final Environment environment;
private final ResourceLoader resourceLoader;
LazyFactoryMethodArgumentComponent(Environment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = resourceLoader;
}
public static LazyFactoryMethodArgumentComponent of(@Lazy Environment environment, ResourceLoader resourceLoader) {
return new LazyFactoryMethodArgumentComponent(environment, resourceLoader);
}
public Environment getEnvironment() {
return this.environment;
}
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
}