@ -17,6 +17,7 @@
@@ -17,6 +17,7 @@
package org.springframework.beans.factory.aot ;
import java.util.List ;
import java.util.function.BiConsumer ;
import javax.lang.model.element.Modifier ;
@ -52,6 +53,10 @@ class BeanRegistrationsAotContribution
@@ -52,6 +53,10 @@ class BeanRegistrationsAotContribution
private static final String BEAN_FACTORY_PARAMETER_NAME = "beanFactory" ;
private static final int MAX_REGISTRATIONS_PER_FILE = 5000 ;
private static final int MAX_REGISTRATIONS_PER_METHOD = 1000 ;
private static final ArgumentCodeGenerator argumentCodeGenerator = ArgumentCodeGenerator
. of ( DefaultListableBeanFactory . class , BEAN_FACTORY_PARAMETER_NAME ) ;
@ -67,14 +72,10 @@ class BeanRegistrationsAotContribution
@@ -67,14 +72,10 @@ class BeanRegistrationsAotContribution
public void applyTo ( GenerationContext generationContext ,
BeanFactoryInitializationCode beanFactoryInitializationCode ) {
GeneratedClass generatedClass = generationContext . getGeneratedClasses ( )
. addForFeature ( "BeanFactoryRegistrations" , type - > {
type . addJavadoc ( "Register bean definitions for the bean factory." ) ;
type . addModifiers ( Modifier . PUBLIC ) ;
} ) ;
GeneratedClass generatedClass = createBeanFactoryRegistrationClass ( generationContext ) ;
BeanRegistrationsCodeGenerator codeGenerator = new BeanRegistrationsCodeGenerator ( generatedClass ) ;
GeneratedMethod generatedBeanDefinitionsMethod = new BeanDefinitionsRegistrationGenerator (
generationContext , codeGenerator , this . registrations ) . generateRegisterBeanDefinitionsMethod ( ) ;
GeneratedMethod generatedBeanDefinitionsMethod = generateBeanRegistrationCode ( generationContext ,
generatedClass , codeGenerator ) ;
beanFactoryInitializationCode . addInitializer ( generatedBeanDefinitionsMethod . toMethodReference ( ) ) ;
GeneratedMethod generatedAliasesMethod = codeGenerator . getMethods ( ) . add ( "registerAliases" ,
this : : generateRegisterAliasesMethod ) ;
@ -82,6 +83,48 @@ class BeanRegistrationsAotContribution
@@ -82,6 +83,48 @@ class BeanRegistrationsAotContribution
generateRegisterHints ( generationContext . getRuntimeHints ( ) , this . registrations ) ;
}
private GeneratedMethod generateBeanRegistrationCode ( GenerationContext generationContext , GeneratedClass mainGeneratedClass , BeanRegistrationsCodeGenerator mainCodeGenerator ) {
if ( this . registrations . size ( ) < MAX_REGISTRATIONS_PER_FILE ) {
return generateBeanRegistrationClass ( generationContext , mainCodeGenerator , 0 , this . registrations . size ( ) ) ;
}
else {
return mainGeneratedClass . getMethods ( ) . add ( "registerBeanDefinitions" , method - > {
method . addJavadoc ( "Register the bean definitions." ) ;
method . addModifiers ( Modifier . PUBLIC ) ;
method . addParameter ( DefaultListableBeanFactory . class , BEAN_FACTORY_PARAMETER_NAME ) ;
CodeBlock . Builder body = CodeBlock . builder ( ) ;
Registration . doWithSlice ( this . registrations , MAX_REGISTRATIONS_PER_FILE , ( start , end ) - > {
GeneratedClass sliceGeneratedClass = createBeanFactoryRegistrationClass ( generationContext ) ;
BeanRegistrationsCodeGenerator sliceCodeGenerator = new BeanRegistrationsCodeGenerator ( sliceGeneratedClass ) ;
GeneratedMethod generatedMethod = generateBeanRegistrationClass ( generationContext , sliceCodeGenerator , start , end ) ;
body . addStatement ( generatedMethod . toMethodReference ( ) . toInvokeCodeBlock ( argumentCodeGenerator ) ) ;
} ) ;
method . addCode ( body . build ( ) ) ;
} ) ;
}
}
private GeneratedMethod generateBeanRegistrationClass ( GenerationContext generationContext ,
BeanRegistrationsCodeGenerator codeGenerator , int start , int end ) {
return codeGenerator . getMethods ( ) . add ( "registerBeanDefinitions" , method - > {
method . addJavadoc ( "Register the bean definitions." ) ;
method . addModifiers ( Modifier . PUBLIC ) ;
method . addParameter ( DefaultListableBeanFactory . class , BEAN_FACTORY_PARAMETER_NAME ) ;
List < Registration > sliceRegistrations = this . registrations . subList ( start , end ) ;
new BeanDefinitionsRegistrationGenerator (
generationContext , codeGenerator , sliceRegistrations , start ) . generateBeanRegistrationsCode ( method ) ;
} ) ;
}
private static GeneratedClass createBeanFactoryRegistrationClass ( GenerationContext generationContext ) {
return generationContext . getGeneratedClasses ( )
. addForFeature ( "BeanFactoryRegistrations" , type - > {
type . addJavadoc ( "Register bean definitions for the bean factory." ) ;
type . addModifiers ( Modifier . PUBLIC ) ;
} ) ;
}
private void generateRegisterAliasesMethod ( MethodSpec . Builder method ) {
method . addJavadoc ( "Register the aliases." ) ;
method . addModifiers ( Modifier . PUBLIC ) ;
@ -117,6 +160,28 @@ class BeanRegistrationsAotContribution
@@ -117,6 +160,28 @@ class BeanRegistrationsAotContribution
return this . registeredBean . getBeanName ( ) ;
}
/ * *
* Invoke an action for each slice of the given { @code registrations } . The
* { @code action } is invoked for each slice with the start and end index of the
* given list of registrations . Elements to process can be retrieved using
* { @link List # subList ( int , int ) } .
* @param registrations the registrations to process
* @param sliceSize the size of a slice
* @param action the action to invoke for each slice
* /
static void doWithSlice ( List < Registration > registrations , int sliceSize ,
BiConsumer < Integer , Integer > action ) {
int index = 0 ;
int end = 0 ;
while ( end < registrations . size ( ) ) {
int start = index * sliceSize ;
end = Math . min ( start + sliceSize , registrations . size ( ) ) ;
action . accept ( start , end ) ;
index + + ;
}
}
}
@ -144,6 +209,10 @@ class BeanRegistrationsAotContribution
@@ -144,6 +209,10 @@ class BeanRegistrationsAotContribution
}
/ * *
* Generate code for bean registrations . Limited to { @value # MAX_REGISTRATIONS_PER_METHOD }
* beans per method to avoid hitting a limit .
* /
static final class BeanDefinitionsRegistrationGenerator {
private final GenerationContext generationContext ;
@ -152,44 +221,38 @@ class BeanRegistrationsAotContribution
@@ -152,44 +221,38 @@ class BeanRegistrationsAotContribution
private final List < Registration > registrations ;
private final int globalStart ;
BeanDefinitionsRegistrationGenerator ( GenerationContext generationContext ,
BeanRegistrationsCodeGenerator codeGenerator , List < Registration > registrations ) {
BeanRegistrationsCodeGenerator codeGenerator , List < Registration > registrations , int globalStart ) {
this . generationContext = generationContext ;
this . codeGenerator = codeGenerator ;
this . registrations = registrations ;
this . globalStart = globalStart ;
}
GeneratedMethod generateRegisterBeanDefinitionsMethod ( ) {
return this . codeGenerator . getMethods ( ) . add ( "registerBeanDefinitions" , method - > {
method . addJavadoc ( "Register the bean definitions." ) ;
method . addModifiers ( Modifier . PUBLIC ) ;
method . addParameter ( DefaultListableBeanFactory . class , BEAN_FACTORY_PARAMETER_NAME ) ;
if ( this . registrations . size ( ) < = 1000 ) {
generateRegisterBeanDefinitionMethods ( method , this . registrations ) ;
}
else {
Builder code = CodeBlock . builder ( ) ;
code . add ( "// Registration is sliced to avoid exceeding size limit\n" ) ;
int index = 0 ;
int end = 0 ;
while ( end < this . registrations . size ( ) ) {
int start = index * 1000 ;
end = Math . min ( start + 1000 , this . registrations . size ( ) ) ;
GeneratedMethod sliceMethod = generateSliceMethod ( start , end ) ;
code . addStatement ( sliceMethod . toMethodReference ( ) . toInvokeCodeBlock (
argumentCodeGenerator , this . codeGenerator . getClassName ( ) ) ) ;
index + + ;
}
method . addCode ( code . build ( ) ) ;
}
} ) ;
void generateBeanRegistrationsCode ( MethodSpec . Builder method ) {
if ( this . registrations . size ( ) < = 1000 ) {
generateRegisterBeanDefinitionMethods ( method , this . registrations ) ;
}
else {
Builder code = CodeBlock . builder ( ) ;
code . add ( "// Registration is sliced to avoid exceeding size limit\n" ) ;
Registration . doWithSlice ( this . registrations , MAX_REGISTRATIONS_PER_METHOD ,
( start , end ) - > {
GeneratedMethod sliceMethod = generateSliceMethod ( start , end ) ;
code . addStatement ( sliceMethod . toMethodReference ( ) . toInvokeCodeBlock (
argumentCodeGenerator , this . codeGenerator . getClassName ( ) ) ) ;
} ) ;
method . addCode ( code . build ( ) ) ;
}
}
private GeneratedMethod generateSliceMethod ( int start , int end ) {
String description = "Register the bean definitions from %s to %s." . formatted ( start , end - 1 ) ;
String description = "Register the bean definitions from %s to %s."
. formatted ( this . globalStart + start , this . globalStart + end - 1 ) ;
List < Registration > slice = this . registrations . subList ( start , end ) ;
return this . codeGenerator . getMethods ( ) . add ( "registerBeanDefinitions" , method - > {
method . addJavadoc ( description ) ;