Enable cglib proxy for configuration classes if necessary
This commit updates code generation to customize the instantiation of a configuration class that requires a proxy. Rather than instantiating the raw class, the proxy is used. Closes gh-29107
This commit is contained in:
@@ -36,14 +36,17 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanFactoryInitializationCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.context.testfixture.context.generator.SimpleComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.ImportAwareConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.SimpleConfiguration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
@@ -78,7 +81,7 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenNoImportAwareConfigurationReturnsNull() {
|
||||
assertThat(getContribution(SimpleConfiguration.class)).isNull();
|
||||
assertThat(getContribution(SimpleComponent.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -323,6 +326,34 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ConfigurationClassProxyTests {
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
private final ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor();
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeRegularConfigurationClass() {
|
||||
assertThat(this.processor.processAheadOfTime(
|
||||
getRegisteredBean(SimpleConfiguration.class))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeFullConfigurationClass() {
|
||||
assertThat(this.processor.processAheadOfTime(
|
||||
getRegisteredBean(CglibConfiguration.class))).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
private RegisteredBean getRegisteredBean(Class<?> bean) {
|
||||
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
|
||||
this.processor.postProcessBeanFactory(this.beanFactory);
|
||||
return RegisteredBean.of(this.beanFactory, "test");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedFiles.Kind;
|
||||
@@ -54,6 +55,7 @@ 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.ConfigurableCglibConfiguration;
|
||||
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;
|
||||
@@ -64,8 +66,10 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
import org.springframework.core.test.tools.Compiled;
|
||||
import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -93,13 +97,13 @@ class ApplicationContextAotGeneratorTests {
|
||||
GenericApplicationContext applicationContext = new GenericApplicationContext();
|
||||
applicationContext.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
applicationContext.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
|
||||
applicationContext.registerBeanDefinition("number",
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class, "valueOf")
|
||||
.addConstructorArgValue("42").getBeanDefinition());
|
||||
.rootBeanDefinition(Integer.class, "valueOf")
|
||||
.addConstructorArgValue("42").getBeanDefinition());
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number");
|
||||
@@ -200,8 +204,8 @@ class ApplicationContextAotGeneratorTests {
|
||||
applicationContext.registerBeanDefinition(
|
||||
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
applicationContext.registerBeanDefinition("initDestroyComponent",
|
||||
new RootBeanDefinition(InitDestroyComponent.class));
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
@@ -220,8 +224,8 @@ class ApplicationContextAotGeneratorTests {
|
||||
applicationContext.registerBeanDefinition(
|
||||
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
|
||||
beanDefinition.setInitMethodName("customInit");
|
||||
beanDefinition.setDestroyMethodName("customDestroy");
|
||||
@@ -270,17 +274,6 @@ class ApplicationContextAotGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWriteProxyAndGenerateReflectionHints() throws IOException {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
String proxyClassName = CglibConfiguration.class.getName() + "$$SpringCGLIB$$0";
|
||||
assertThat(context.getGeneratedFiles()
|
||||
.getGeneratedFileContent(Kind.CLASS, proxyClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(proxyClassName))
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWithPropertySource() {
|
||||
@@ -295,8 +288,51 @@ class ApplicationContextAotGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Nested
|
||||
@CompileWithForkedClassLoader
|
||||
class ConfigurationClassCglibProxy {
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWriteProxyAndGenerateReflectionHints() throws IOException {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
String proxyClassName = CglibConfiguration.class.getName() + "$$SpringCGLIB$$0";
|
||||
assertThat(context.getGeneratedFiles()
|
||||
.getGeneratedFileContent(Kind.CLASS, proxyClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(proxyClassName))
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext.getBean("prefix", String.class)).isEqualTo("Hello0");
|
||||
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hello0 World");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWithArgumentsUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(ConfigurableCglibConfiguration.class);
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = createFreshApplicationContext(initializer);
|
||||
freshApplicationContext.setEnvironment(new MockEnvironment().withProperty("test.prefix", "Hi"));
|
||||
freshApplicationContext.refresh();
|
||||
assertThat(freshApplicationContext.getBean("prefix", String.class)).isEqualTo("Hi0");
|
||||
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hi0 World");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {
|
||||
return hints -> assertThat(hints).noneMatch(hint -> hint.getProxiedInterfaces().get(0).equals(target));
|
||||
return hints -> assertThat(hints).noneMatch(hint ->
|
||||
hint.getProxiedInterfaces().get(0).equals(TypeReference.of(target)));
|
||||
}
|
||||
|
||||
private static TestGenerationContext processAheadOfTime(GenericApplicationContext applicationContext) {
|
||||
@@ -307,23 +343,29 @@ class ApplicationContextAotGeneratorTests {
|
||||
return generationContext;
|
||||
}
|
||||
|
||||
private void testCompiledResult(GenericApplicationContext applicationContext,
|
||||
private static void testCompiledResult(GenericApplicationContext applicationContext,
|
||||
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
|
||||
testCompiledResult(processAheadOfTime(applicationContext), result);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void testCompiledResult(TestGenerationContext generationContext,
|
||||
private static void testCompiledResult(TestGenerationContext generationContext,
|
||||
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
|
||||
TestCompiler.forSystem().with(generationContext).compile(compiled ->
|
||||
result.accept(compiled.getInstance(ApplicationContextInitializer.class), compiled));
|
||||
}
|
||||
|
||||
private GenericApplicationContext toFreshApplicationContext(
|
||||
private static GenericApplicationContext toFreshApplicationContext(
|
||||
ApplicationContextInitializer<GenericApplicationContext> initializer) {
|
||||
GenericApplicationContext freshApplicationContext = createFreshApplicationContext(initializer);
|
||||
freshApplicationContext.refresh();
|
||||
return freshApplicationContext;
|
||||
}
|
||||
|
||||
private static GenericApplicationContext createFreshApplicationContext(
|
||||
ApplicationContextInitializer<GenericApplicationContext> initializer) {
|
||||
GenericApplicationContext freshApplicationContext = new GenericApplicationContext();
|
||||
initializer.initialize(freshApplicationContext);
|
||||
freshApplicationContext.refresh();
|
||||
return freshApplicationContext;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user