Avoid code too large with AOT processing

This commit adapts code generation to "slice" the registration of bean
definitions in separate bean methods rather than a unique method for
all of them.

If the bean factory has more than a thousand bean, a method is created
for each slice of 1000 bean definitions.

Closes gh-33126
This commit is contained in:
Stéphane Nicoll
2024-07-17 16:02:41 +02:00
parent 48dead4017
commit 30a64d6a0b
2 changed files with 145 additions and 29 deletions

View File

@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.lang.model.element.Modifier;
@@ -210,6 +211,59 @@ class BeanRegistrationsAotContributionTests {
.havingCause().isInstanceOf(IllegalStateException.class).withMessage("Test exception");
}
@Test
void applyToWithLessThanAThousandBeanDefinitionsDoesNotCreateSlices() {
BeanRegistrationsAotContribution contribution = createContribution(999, i -> "testBean" + i);
contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode);
compile((consumer, compiled) -> {
assertThat(compiled.getSourceFile(".*BeanFactoryRegistrations"))
.doesNotContain("Register the bean definitions from 0 to 999.",
"// Registration is sliced to avoid exceeding size limit");
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory();
consumer.accept(freshBeanFactory);
for (int i = 0; i < 999; i++) {
String beanName = "testBean" + i;
assertThat(freshBeanFactory.containsBeanDefinition(beanName)).isTrue();
assertThat(freshBeanFactory.getBean(beanName)).isInstanceOf(TestBean.class);
}
assertThat(freshBeanFactory.getBeansOfType(TestBean.class)).hasSize(999);
});
}
@Test
void applyToWithLargeBeanDefinitionsCreatesSlices() {
BeanRegistrationsAotContribution contribution = createContribution(1001, i -> "testBean" + i);
contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode);
compile((consumer, compiled) -> {
assertThat(compiled.getSourceFile(".*BeanFactoryRegistrations"))
.contains("Register the bean definitions from 0 to 999.",
"Register the bean definitions from 1000 to 1000.",
"// Registration is sliced to avoid exceeding size limit");
DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory();
consumer.accept(freshBeanFactory);
for (int i = 0; i < 1001; i++) {
String beanName = "testBean" + i;
assertThat(freshBeanFactory.containsBeanDefinition(beanName)).isTrue();
assertThat(freshBeanFactory.getBean(beanName)).isInstanceOf(TestBean.class);
}
assertThat(freshBeanFactory.getBeansOfType(TestBean.class)).hasSize(1001);
});
}
private BeanRegistrationsAotContribution createContribution(int size, Function<Integer, String> beanNameFactory) {
List<Registration> registrations = new ArrayList<>();
for (int i = 0; i < size; i++) {
String beanName = beanNameFactory.apply(i);
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, beanName);
BeanDefinitionMethodGenerator methodGenerator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null, List.of());
registrations.add(new Registration(registeredBean, methodGenerator, new String[0]));
}
return new BeanRegistrationsAotContribution(registrations);
}
private RegisteredBean registerBean(RootBeanDefinition rootBeanDefinition) {
String beanName = "testBean";
this.beanFactory.registerBeanDefinition(beanName, rootBeanDefinition);