From 7820804bf6d07635d6f28c607ecde9243db4628f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 14 Apr 2022 15:10:01 +0200 Subject: [PATCH] Allow registration of bean factory contributors via spring.factories Closes gh-28342 --- .../ApplicationContextAotGenerator.java | 4 +- .../main/resources/META-INF/spring.factories | 2 + .../ApplicationContextAotGeneratorTests.java | 59 ++++++++++++++++++- .../aot/bean-factory-contributors.factories | 3 + 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 spring-context/src/main/resources/META-INF/spring.factories create mode 100644 spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories diff --git a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java index 5159e21cb62..3a96e0b23ae 100644 --- a/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/generator/ApplicationContextAotGenerator.java @@ -42,6 +42,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.OrderComparator; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.javapoet.CodeBlock; import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.ParameterizedTypeName; @@ -150,7 +151,8 @@ public class ApplicationContextAotGenerator { for (String ppName : postProcessorNames) { postProcessors.add(beanFactory.getBean(ppName, AotContributingBeanFactoryPostProcessor.class)); } - postProcessors.add(new RuntimeHintsPostProcessor()); + postProcessors.addAll(SpringFactoriesLoader.loadFactories(AotContributingBeanFactoryPostProcessor.class, + beanFactory.getBeanClassLoader())); sortPostProcessors(postProcessors, beanFactory); return postProcessors; } diff --git a/spring-context/src/main/resources/META-INF/spring.factories b/spring-context/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000000..5a57309789f --- /dev/null +++ b/spring-context/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor= \ +org.springframework.context.generator.RuntimeHintsPostProcessor \ No newline at end of file diff --git a/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java b/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java index 5f13b7665e1..b87804d7378 100644 --- a/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/context/generator/ApplicationContextAotGeneratorTests.java @@ -18,6 +18,8 @@ package org.springframework.context.generator; import java.io.IOException; import java.io.StringWriter; +import java.net.URL; +import java.util.Enumeration; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Supplier; @@ -148,6 +150,27 @@ class ApplicationContextAotGeneratorTests { """); } + @Test + void generateApplicationContextLoadsBeanFactoryContributors() { + GeneratedTypeContext generationContext = createGenerationContext(); + ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); + GenericApplicationContext applicationContext = new GenericApplicationContext(); + applicationContext.setClassLoader( + new TestSpringFactoriesClassLoader("bean-factory-contributors.factories")); + generator.generateApplicationContext(applicationContext, generationContext); + assertThat(write(generationContext.getMainGeneratedType())).contains(""" + public class Test implements ApplicationContextInitializer { + @Override + public void initialize(GenericApplicationContext context) { + // infrastructure + DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); + beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); + // Test + } + } + """); + } + @Test void generateApplicationContextApplyContributionAsIsWithNewLineAtTheEnd() { GenericApplicationContext applicationContext = new GenericApplicationContext(); @@ -211,8 +234,7 @@ class ApplicationContextAotGeneratorTests { GeneratedTypeContext generationContext = createGenerationContext(); GenericApplicationContext applicationContext = new GenericApplicationContext(); DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory(); - @SuppressWarnings("unchecked") - BiPredicate excludeFilter = mock(BiPredicate.class); + BiPredicate excludeFilter = mockExcludeFilter(); given(excludeFilter.test(eq("bean1"), any(BeanDefinition.class))).willReturn(Boolean.FALSE); given(excludeFilter.test(eq("bean2"), any(BeanDefinition.class))).willReturn(Boolean.TRUE); applicationContext.registerBeanDefinition("bean2", new RootBeanDefinition(SimpleComponent.class)); @@ -228,6 +250,11 @@ class ApplicationContextAotGeneratorTests { } + @SuppressWarnings("unchecked") + private BiPredicate mockExcludeFilter() { + return mock(BiPredicate.class); + } + @SuppressWarnings("rawtypes") private void compile(GenericApplicationContext applicationContext, Consumer initializer) { DefaultGeneratedTypeContext generationContext = createGenerationContext(); @@ -306,6 +333,15 @@ class ApplicationContextAotGeneratorTests { } + static class TextAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor { + + @Override + public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) { + return initialization -> initialization.contribute(code -> code.add("// Test\n")); + } + + } + static class NoOpAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor { @Override @@ -352,4 +388,23 @@ class ApplicationContextAotGeneratorTests { } + static class TestSpringFactoriesClassLoader extends ClassLoader { + + private final String factoriesName; + + TestSpringFactoriesClassLoader(String factoriesName) { + super(RuntimeHintsPostProcessorTests.class.getClassLoader()); + this.factoriesName = factoriesName; + } + + @Override + public Enumeration getResources(String name) throws IOException { + if ("META-INF/spring.factories".equals(name)) { + return super.getResources("org/springframework/context/generator/aot/" + this.factoriesName); + } + return super.getResources(name); + } + + } + } diff --git a/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories b/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories new file mode 100644 index 00000000000..36475ad5baa --- /dev/null +++ b/spring-context/src/test/resources/org/springframework/context/generator/aot/bean-factory-contributors.factories @@ -0,0 +1,3 @@ +org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor= \ +org.springframework.context.generator.ApplicationContextAotGeneratorTests.NoOpAotContributingBeanFactoryPostProcessor, \ +org.springframework.context.generator.ApplicationContextAotGeneratorTests.TextAotContributingBeanFactoryPostProcessor \ No newline at end of file