From 458418be29e8ae3cbb84930e45a3e720c0d816d7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 29 Sep 2023 17:53:25 +0100 Subject: [PATCH] Automatically register reflection hints for scanned WebListeners Closes gh-36008 --- ...vletComponentRegisteringPostProcessor.java | 35 ++++++++++++++++++- .../ServletComponentScanRegistrar.java | 11 ------ .../resources/META-INF/spring/aot.factories | 3 -- .../ServletComponentScanIntegrationTests.java | 13 +++---- .../ServletComponentScanRegistrarTests.java | 23 ++++++++++++ .../{ => filter}/TestFilter.java | 4 +-- .../{ => listener}/TestListener.java | 2 +- .../{ => servlet}/TestMultipartServlet.java | 2 +- .../{ => servlet}/TestServlet.java | 2 +- 9 files changed, 69 insertions(+), 26 deletions(-) rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/{ => filter}/TestFilter.java (92%) rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/{ => listener}/TestListener.java (96%) rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/{ => servlet}/TestMultipartServlet.java (93%) rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/{ => servlet}/TestServlet.java (94%) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java index dcd825836e..63b36ad9de 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.java @@ -19,10 +19,17 @@ package org.springframework.boot.web.servlet; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; +import org.springframework.aot.generate.GenerationContext; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.TypeReference; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; +import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; +import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -40,7 +47,8 @@ import org.springframework.web.context.WebApplicationContext; * @see ServletComponentScan * @see ServletComponentScanRegistrar */ -class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { +class ServletComponentRegisteringPostProcessor + implements BeanFactoryPostProcessor, ApplicationContextAware, BeanFactoryInitializationAotProcessor { private static final List HANDLERS; @@ -105,4 +113,29 @@ class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcess this.applicationContext = applicationContext; } + @Override + public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { + return new BeanFactoryInitializationAotContribution() { + + @Override + public void applyTo(GenerationContext generationContext, + BeanFactoryInitializationCode beanFactoryInitializationCode) { + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition definition = beanFactory.getBeanDefinition(beanName); + if (Objects.equals(definition.getBeanClassName(), + WebListenerHandler.ServletComponentWebListenerRegistrar.class.getName())) { + String listenerClassName = (String) definition.getConstructorArgumentValues() + .getArgumentValue(0, String.class) + .getValue(); + generationContext.getRuntimeHints() + .reflection() + .registerType(TypeReference.of(listenerClassName), + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + } + } + } + + }; + } + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrar.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrar.java index 188e8a9b65..49d548f16b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrar.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrar.java @@ -22,11 +22,9 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.function.Supplier; -import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotationMetadata; @@ -102,13 +100,4 @@ class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar { } - static class ServletComponentScanBeanRegistrationExcludeFilter implements BeanRegistrationExcludeFilter { - - @Override - public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { - return BEAN_NAME.equals(registeredBean.getBeanName()); - } - - } - } diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories index 7996bc7918..905cd71740 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories @@ -20,6 +20,3 @@ org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryIni org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrationAotProcessor,\ org.springframework.boot.jackson.JsonMixinModuleEntriesBeanRegistrationAotProcessor - -org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter=\ -org.springframework.boot.web.servlet.ServletComponentScanRegistrar.ServletComponentScanBeanRegistrationExcludeFilter \ No newline at end of file diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java index bbc5e0a06f..b25dd3eefa 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java @@ -46,7 +46,10 @@ import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFa import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; -import org.springframework.boot.web.servlet.testcomponents.TestMultipartServlet; +import org.springframework.boot.web.servlet.testcomponents.filter.TestFilter; +import org.springframework.boot.web.servlet.testcomponents.listener.TestListener; +import org.springframework.boot.web.servlet.testcomponents.servlet.TestMultipartServlet; +import org.springframework.boot.web.servlet.testcomponents.servlet.TestServlet; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @@ -128,11 +131,9 @@ class ServletComponentScanIntegrationTests { File metaInf = new File(temp, "META-INF"); metaInf.mkdirs(); Properties index = new Properties(); - index.setProperty("org.springframework.boot.web.servlet.testcomponents.TestFilter", WebFilter.class.getName()); - index.setProperty("org.springframework.boot.web.servlet.testcomponents.TestListener", - WebListener.class.getName()); - index.setProperty("org.springframework.boot.web.servlet.testcomponents.TestServlet", - WebServlet.class.getName()); + index.setProperty(TestFilter.class.getName(), WebFilter.class.getName()); + index.setProperty(TestListener.class.getName(), WebListener.class.getName()); + index.setProperty(TestServlet.class.getName(), WebServlet.class.getName()); try (FileWriter writer = new FileWriter(new File(metaInf, "spring.components"))) { index.store(writer, null); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrarTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrarTests.java index ff784ba9b8..c3525e4903 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrarTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanRegistrarTests.java @@ -21,8 +21,12 @@ import java.util.function.Consumer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.testcomponents.listener.TestListener; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; @@ -141,6 +145,19 @@ class ServletComponentScanRegistrarTests { }); } + @Test + void processAheadOfTimeRegistersReflectionHintsForWebListeners() { + AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); + context.registerBean(ScanListenerPackage.class); + TestGenerationContext generationContext = new TestGenerationContext( + ClassName.get(getClass().getPackageName(), "TestTarget")); + new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext); + assertThat(RuntimeHintsPredicates.reflection() + .onType(TestListener.class) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)) + .accepts(generationContext.getRuntimeHints()); + } + @SuppressWarnings("unchecked") private void compile(GenericApplicationContext context, Consumer freshContext) { TestGenerationContext generationContext = new TestGenerationContext( @@ -192,4 +209,10 @@ class ServletComponentScanRegistrarTests { } + @Configuration(proxyBeanMethods = false) + @ServletComponentScan("org.springframework.boot.web.servlet.testcomponents.listener") + static class ScanListenerPackage { + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestFilter.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/filter/TestFilter.java similarity index 92% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestFilter.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/filter/TestFilter.java index 8e734aef2d..d9b1578938 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestFilter.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/filter/TestFilter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.web.servlet.testcomponents; +package org.springframework.boot.web.servlet.testcomponents.filter; import java.io.IOException; @@ -27,7 +27,7 @@ import jakarta.servlet.ServletResponse; import jakarta.servlet.annotation.WebFilter; @WebFilter("/*") -class TestFilter implements Filter { +public class TestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/listener/TestListener.java similarity index 96% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/listener/TestListener.java index 0047904791..48e2d01716 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestListener.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/listener/TestListener.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.web.servlet.testcomponents; +package org.springframework.boot.web.servlet.testcomponents.listener; import java.io.IOException; import java.util.EnumSet; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestMultipartServlet.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestMultipartServlet.java similarity index 93% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestMultipartServlet.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestMultipartServlet.java index 1985e452a9..f5c2ef5c6c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestMultipartServlet.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestMultipartServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.web.servlet.testcomponents; +package org.springframework.boot.web.servlet.testcomponents.servlet; import jakarta.servlet.annotation.MultipartConfig; import jakarta.servlet.annotation.WebServlet; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestServlet.java similarity index 94% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestServlet.java index d9c0844b99..24a230c6d2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/TestServlet.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/testcomponents/servlet/TestServlet.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.web.servlet.testcomponents; +package org.springframework.boot.web.servlet.testcomponents.servlet; import java.io.IOException;