Introduce ApplicationContextAotGenerator
This commit introduces a way to process a GenericApplicationContext ahead of time. Components that can contribute in that phase are invoked, and their contributions are recorded in the GeneratedTypeContext. This commit also expands BeanFactoryContribution so that it can exclude bean definitions that are no longer required. Closes gh-28150
This commit is contained in:
@@ -20,8 +20,11 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
@@ -69,6 +72,16 @@ public class BeanDefinitionsContribution implements BeanFactoryContribution {
|
||||
writeBeanDefinitions(initialization);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiPredicate<String, BeanDefinition> getBeanDefinitionExcludeFilter() {
|
||||
List<BiPredicate<String, BeanDefinition>> predicates = new ArrayList<>();
|
||||
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
|
||||
handleMergedBeanDefinition(beanName, beanDefinition -> predicates.add(
|
||||
getBeanRegistrationContribution(beanName, beanDefinition).getBeanDefinitionExcludeFilter()));
|
||||
}
|
||||
return predicates.stream().filter(Objects::nonNull).reduce((n, d) -> false, BiPredicate::or);
|
||||
}
|
||||
|
||||
private void writeBeanDefinitions(BeanFactoryInitialization initialization) {
|
||||
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
|
||||
handleMergedBeanDefinition(beanName, beanDefinition -> {
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.beans.factory.generator;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
||||
/**
|
||||
* Contribute optimizations ahead of time to initialize a bean factory.
|
||||
*
|
||||
@@ -31,4 +35,14 @@ public interface BeanFactoryContribution {
|
||||
*/
|
||||
void applyTo(BeanFactoryInitialization initialization);
|
||||
|
||||
/**
|
||||
* Return a predicate that determines if a particular bean definition
|
||||
* should be excluded from processing. Can be used to exclude infrastructure
|
||||
* that has been optimized using generated code.
|
||||
* @return the predicate to use
|
||||
*/
|
||||
default BiPredicate<String, BeanDefinition> getBeanDefinitionExcludeFilter() {
|
||||
return (beanName, beanDefinition) -> false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.beans.factory.generator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatchers;
|
||||
@@ -26,6 +27,7 @@ import org.mockito.Mockito;
|
||||
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
|
||||
import org.springframework.aot.generator.GeneratedType;
|
||||
import org.springframework.aot.generator.GeneratedTypeContext;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -90,6 +92,37 @@ class BeanDefinitionsContributionTests {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getBeanDefinitionWithNoUnderlyingContributorReturnFalseByDefault() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
BiPredicate<String, BeanDefinition> excludeFilter = new BeanDefinitionsContribution(beanFactory)
|
||||
.getBeanDefinitionExcludeFilter();
|
||||
assertThat(excludeFilter.test("foo", new RootBeanDefinition())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
void getBeanDefinitionExcludeFilterWrapsUnderlyingFilter() {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("bean1", new RootBeanDefinition());
|
||||
beanFactory.registerBeanDefinition("bean2", new RootBeanDefinition());
|
||||
BiPredicate<String, BeanDefinition> excludeFilter1 = Mockito.mock(BiPredicate.class);
|
||||
BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE);
|
||||
BDDMockito.given(excludeFilter1.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.FALSE);
|
||||
BiPredicate<String, BeanDefinition> excludeFilter2 = Mockito.mock(BiPredicate.class);
|
||||
BDDMockito.given(excludeFilter2.test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class))).willReturn(Boolean.TRUE);
|
||||
BiPredicate<String, BeanDefinition> excludeFilter = new BeanDefinitionsContribution(beanFactory, List.of(
|
||||
new TestBeanRegistrationContributionProvider("bean1", mockExcludeFilter(excludeFilter1)),
|
||||
new TestBeanRegistrationContributionProvider("bean2", mockExcludeFilter(excludeFilter2)))
|
||||
).getBeanDefinitionExcludeFilter();
|
||||
assertThat(excludeFilter.test("bean2", new RootBeanDefinition())).isTrue();
|
||||
Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
Mockito.verify(excludeFilter2).test(ArgumentMatchers.eq("bean2"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
assertThat(excludeFilter.test("bean1", new RootBeanDefinition())).isTrue();
|
||||
Mockito.verify(excludeFilter1).test(ArgumentMatchers.eq("bean1"), ArgumentMatchers.any(BeanDefinition.class));
|
||||
Mockito.verifyNoMoreInteractions(excludeFilter2);
|
||||
}
|
||||
|
||||
private CodeSnippet contribute(DefaultListableBeanFactory beanFactory, GeneratedTypeContext generationContext) {
|
||||
BeanDefinitionsContribution contribution = new BeanDefinitionsContribution(beanFactory);
|
||||
BeanFactoryInitialization initialization = new BeanFactoryInitialization(generationContext);
|
||||
@@ -102,6 +135,12 @@ class BeanDefinitionsContributionTests {
|
||||
GeneratedType.of(ClassName.get(packageName, "Test")));
|
||||
}
|
||||
|
||||
private BeanFactoryContribution mockExcludeFilter(BiPredicate<String, BeanDefinition> excludeFilter) {
|
||||
BeanFactoryContribution contribution = Mockito.mock(BeanFactoryContribution.class);
|
||||
BDDMockito.given(contribution.getBeanDefinitionExcludeFilter()).willReturn(excludeFilter);
|
||||
return contribution;
|
||||
}
|
||||
|
||||
static class TestBeanRegistrationContributionProvider implements BeanRegistrationContributionProvider {
|
||||
|
||||
private final String beanName;
|
||||
|
||||
Reference in New Issue
Block a user