From 9413383d7285ad1864f91bbb515caea148c09b3b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 28 Jul 2022 13:48:52 +0100 Subject: [PATCH] Enforce BeanRegistrationExcludeFilter beans are also AOT processors Update `BeanDefinitionMethodGeneratorFactory` to enforce that any `BeanRegistrationExcludeFilter` filter that is from a bean factory also implements an AOT processor interface. See gh-28866 --- .../BeanDefinitionMethodGeneratorFactory.java | 12 ++++++- .../aot/BeanRegistrationExcludeFilter.java | 2 +- ...DefinitionMethodGeneratorFactoryTests.java | 33 +++++++++++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java index bde55493f4..c7258ae6cc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java @@ -22,10 +22,12 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.aot.AotServices.Source; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.core.log.LogMessage; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** @@ -65,6 +67,14 @@ class BeanDefinitionMethodGeneratorFactory { BeanDefinitionMethodGeneratorFactory(AotServices.Loader loader) { this.aotProcessors = loader.load(BeanRegistrationAotProcessor.class); this.excludeFilters = loader.load(BeanRegistrationExcludeFilter.class); + for (BeanRegistrationExcludeFilter excludeFilter : this.excludeFilters) { + if (this.excludeFilters.getSource(excludeFilter) == Source.BEAN_FACTORY) { + Assert.state(excludeFilter instanceof BeanRegistrationAotProcessor + || excludeFilter instanceof BeanFactoryInitializationAotProcessor, + () -> "BeanRegistrationExcludeFilter bean of type %s must also implement an AOT processor interface" + .formatted(excludeFilter.getClass().getName())); + } + } } @@ -97,7 +107,7 @@ class BeanDefinitionMethodGeneratorFactory { return true; } for (BeanRegistrationExcludeFilter excludeFilter : this.excludeFilters) { - if (excludeFilter.isExcluded(registeredBean)) { + if (excludeFilter.isExcludedFromAotProcessing(registeredBean)) { logger.trace(LogMessage.format( "Excluding registered bean '%s' from bean factory %s due to %s", registeredBean.getBeanName(), diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationExcludeFilter.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationExcludeFilter.java index a4da8642e8..10812417bc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationExcludeFilter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationExcludeFilter.java @@ -35,6 +35,6 @@ public interface BeanRegistrationExcludeFilter { * @param registeredBean the registered bean * @return if the registered bean should be excluded */ - boolean isExcluded(RegisteredBean registeredBean); + boolean isExcludedFromAotProcessing(RegisteredBean registeredBean); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java index 6d922ec216..1a357389f3 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java @@ -26,6 +26,8 @@ import org.springframework.core.Ordered; import org.springframework.core.mock.MockSpringFactoriesLoader; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.Assertions.assertThatNoException; import static org.mockito.Mockito.mock; /** @@ -35,6 +37,25 @@ import static org.mockito.Mockito.mock; */ class BeanDefinitionMethodGeneratorFactoryTests { + @Test + void createWhenBeanRegistrationExcludeFilterBeanIsNotAotProcessorThrowsException() { + BeanRegistrationExcludeFilter filter = registeredBean -> false; + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerSingleton("filter", filter); + assertThatIllegalStateException() + .isThrownBy(() -> new BeanDefinitionMethodGeneratorFactory(beanFactory)) + .withMessageContaining("also implement an AOT processor interface"); + } + + @Test + void createWhenBeanRegistrationExcludeFilterFactoryIsNotAotProcessorLoads() { + BeanRegistrationExcludeFilter filter = registeredBean -> false; + MockSpringFactoriesLoader loader = new MockSpringFactoriesLoader(); + loader.addInstance(BeanRegistrationExcludeFilter.class, filter); + assertThatNoException().isThrownBy(() -> new BeanDefinitionMethodGeneratorFactory( + AotServices.factories(loader))); + } + @Test void getBeanDefinitionMethodGeneratorWhenExcludedByBeanRegistrationExcludeFilterReturnsNull() { MockSpringFactoriesLoader springFactoriesLoader = new MockSpringFactoriesLoader(); @@ -144,8 +165,8 @@ class BeanDefinitionMethodGeneratorFactoryTests { return RegisteredBean.of(beanFactory, "test"); } - static class MockBeanRegistrationExcludeFilter - implements BeanRegistrationExcludeFilter, Ordered { + static class MockBeanRegistrationExcludeFilter implements + BeanRegistrationAotProcessor, BeanRegistrationExcludeFilter, Ordered { private final boolean excluded; @@ -159,7 +180,13 @@ class BeanDefinitionMethodGeneratorFactoryTests { } @Override - public boolean isExcluded(RegisteredBean registeredBean) { + public BeanRegistrationAotContribution processAheadOfTime( + RegisteredBean registeredBean) { + return null; + } + + @Override + public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { this.registeredBean = registeredBean; return this.excluded; }