From 172102d225c916e2ca24b87aa0f0a74d824815b2 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 4 Jun 2022 11:57:02 -0700 Subject: [PATCH] Refine ApplicationContextAotGenerator class name generation Refine the class name logic so that the name is passed in rather than using `ApplicationContext.getId()`. Also propagate the name so that the generated classes use it. See gh-28565 --- .../aot/BeanDefinitionMethodGenerator.java | 13 +++--- .../BeanDefinitionMethodGeneratorFactory.java | 1 + .../aot/BeanFactoryInitializationCode.java | 6 +-- .../aot/BeanRegistrationsAotContribution.java | 7 ++-- .../DefaultBeanRegistrationCodeFragments.java | 8 +++- .../BeanDefinitionMethodGeneratorTests.java | 20 ++++----- ...BeanRegistrationsAotContributionTests.java | 41 +++++++++++++++++-- .../aot/ApplicationContextAotGenerator.java | 9 ++-- ...ionContextInitializationCodeGenerator.java | 11 ++--- 9 files changed, 81 insertions(+), 35 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java index bc3105b0b8..37d75fd0cb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java @@ -81,19 +81,21 @@ class BeanDefinitionMethodGenerator { * Generate the method that returns the {@link BeanDefinition} to be * registered. * @param generationContext the generation context + * @param featureNamePrefix the prefix to use for the feature name * @param beanRegistrationsCode the bean registrations code * @return a reference to the generated method. */ MethodReference generateBeanDefinitionMethod(GenerationContext generationContext, - BeanRegistrationsCode beanRegistrationsCode) { + String featureNamePrefix, BeanRegistrationsCode beanRegistrationsCode) { - BeanRegistrationCodeFragments codeFragments = getCodeFragments(beanRegistrationsCode); + BeanRegistrationCodeFragments codeFragments = getCodeFragments(beanRegistrationsCode, featureNamePrefix); Class target = codeFragments.getTarget(this.registeredBean, this.constructorOrFactoryMethod); if (!target.getName().startsWith("java.")) { + String featureName = featureNamePrefix + "BeanDefinitions"; GeneratedClass generatedClass = generationContext.getClassGenerator() .getOrGenerateClass(new BeanDefinitionsJavaFileGenerator(target), - target, "BeanDefinitions"); + target, featureName); MethodGenerator methodGenerator = generatedClass.getMethodGenerator() .withName(getName()); GeneratedMethod generatedMethod = generateBeanDefinitionMethod( @@ -112,10 +114,11 @@ class BeanDefinitionMethodGenerator { } private BeanRegistrationCodeFragments getCodeFragments( - BeanRegistrationsCode beanRegistrationsCode) { + BeanRegistrationsCode beanRegistrationsCode, String featureNamePrefix) { BeanRegistrationCodeFragments codeFragments = new DefaultBeanRegistrationCodeFragments( - beanRegistrationsCode, this.registeredBean, this.methodGeneratorFactory); + beanRegistrationsCode, this.registeredBean, this.methodGeneratorFactory, + featureNamePrefix); for (BeanRegistrationAotContribution aotContribution : this.aotContributions) { codeFragments = aotContribution.customizeBeanRegistrationCodeFragments(codeFragments); } 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 93467ff8ea..341b76366e 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 @@ -75,6 +75,7 @@ class BeanDefinitionMethodGeneratorFactory { * {@link BeanDefinitionMethodGenerator} will include all * {@link BeanRegistrationAotProcessor} provided contributions. * @param registeredBean the registered bean + * @param innerBeanPropertyName the inner bean property name or {@code null} * @return a new {@link BeanDefinitionMethodGenerator} instance or * {@code null} */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanFactoryInitializationCode.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanFactoryInitializationCode.java index f835cc284d..ceefb8e2e6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanFactoryInitializationCode.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanFactoryInitializationCode.java @@ -46,10 +46,10 @@ public interface BeanFactoryInitializationCode { } /** - * Return the ID of the bean factory or and empty string if no ID is avaialble. - * @return the bean factory ID + * Return the name of the bean factory or and empty string if no ID is available. + * @return the bean factory name */ - default String getId() { + default String getName() { return ""; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java index feb13d01c9..52e661b2c1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContribution.java @@ -63,12 +63,13 @@ class BeanRegistrationsAotContribution ClassName className = generationContext.getClassNameGenerator().generateClassName( beanFactoryInitializationCode.getTarget(), - beanFactoryInitializationCode.getId() + "BeanFactoryRegistrations"); + beanFactoryInitializationCode.getName() + "BeanFactoryRegistrations"); BeanRegistrationsCodeGenerator codeGenerator = new BeanRegistrationsCodeGenerator( className); GeneratedMethod registerMethod = codeGenerator.getMethodGenerator() .generateMethod("registerBeanDefinitions") .using(builder -> generateRegisterMethod(builder, generationContext, + beanFactoryInitializationCode.getName(), codeGenerator)); JavaFile javaFile = codeGenerator.generatedJavaFile(className); generationContext.getGeneratedFiles().addSourceFile(javaFile); @@ -77,7 +78,7 @@ class BeanRegistrationsAotContribution } private void generateRegisterMethod(MethodSpec.Builder builder, - GenerationContext generationContext, + GenerationContext generationContext, String featureNamePrefix, BeanRegistrationsCode beanRegistrationsCode) { builder.addJavadoc("Register the bean definitions."); @@ -87,7 +88,7 @@ class BeanRegistrationsAotContribution CodeBlock.Builder code = CodeBlock.builder(); this.registrations.forEach((beanName, beanDefinitionMethodGenerator) -> { MethodReference beanDefinitionMethod = beanDefinitionMethodGenerator - .generateBeanDefinitionMethod(generationContext, + .generateBeanDefinitionMethod(generationContext, featureNamePrefix, beanRegistrationsCode); code.addStatement("$L.registerBeanDefinition($S, $L)", BEAN_FACTORY_PARAMETER_NAME, beanName, diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/DefaultBeanRegistrationCodeFragments.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/DefaultBeanRegistrationCodeFragments.java index 4f00fa68a4..f5f5137f0f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/DefaultBeanRegistrationCodeFragments.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/DefaultBeanRegistrationCodeFragments.java @@ -54,14 +54,18 @@ class DefaultBeanRegistrationCodeFragments extends BeanRegistrationCodeFragments private final BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory; + private final String featureNamePrefix; + DefaultBeanRegistrationCodeFragments(BeanRegistrationsCode beanRegistrationsCode, RegisteredBean registeredBean, - BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory) { + BeanDefinitionMethodGeneratorFactory beanDefinitionMethodGeneratorFactory, + String featureNamePrefix) { this.beanRegistrationsCode = beanRegistrationsCode; this.registeredBean = registeredBean; this.beanDefinitionMethodGeneratorFactory = beanDefinitionMethodGeneratorFactory; + this.featureNamePrefix = featureNamePrefix; } @@ -120,7 +124,7 @@ class DefaultBeanRegistrationCodeFragments extends BeanRegistrationCodeFragments .getBeanDefinitionMethodGenerator(innerRegisteredBean, name); Assert.state(methodGenerator != null, "Unexpected filtering of inner-bean"); MethodReference generatedMethod = methodGenerator - .generateBeanDefinitionMethod(generationContext, + .generateBeanDefinitionMethod(generationContext, this.featureNamePrefix, this.beanRegistrationsCode); return generatedMethod.toInvokeCodeBlock(); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java index 69f704daf4..382240cb79 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java @@ -98,7 +98,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); assertThat(sourceFile).contains("Get the bean definition for 'testBean'"); @@ -116,7 +116,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { assertThat(actual.getResolvableType().resolve()).isEqualTo(GenericBean.class); SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); @@ -149,7 +149,7 @@ class BeanDefinitionMethodGeneratorTests { BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator( this.methodGeneratorFactory, registeredBean, null, aotContributions); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { assertThat(actual.getBeanClass()).isEqualTo(TestBean.class); InstanceSupplier supplier = (InstanceSupplier) actual @@ -175,7 +175,7 @@ class BeanDefinitionMethodGeneratorTests { BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator( this.methodGeneratorFactory, registeredBean, null, aotContributions); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { assertThat(actual.getBeanClass()).isEqualTo(TestBean.class); SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); @@ -215,7 +215,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, aotContributions); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { assertThat(actual.getAttribute("a")).isEqualTo("A"); assertThat(actual.getAttribute("b")).isNull(); @@ -248,7 +248,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, innerBean, "testInnerBean", Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { assertThat(compiled.getSourceFile(".*BeanDefinitions")) .contains("Get the inner-bean definition for 'testInnerBean'"); @@ -269,7 +269,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { RootBeanDefinition actualInnerBeanDefinition = (RootBeanDefinition) actual .getPropertyValues().get("name"); @@ -303,7 +303,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { RootBeanDefinition actualInnerBeanDefinition = (RootBeanDefinition) actual .getConstructorArgumentValues() @@ -336,7 +336,7 @@ class BeanDefinitionMethodGeneratorTests { BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator( this.methodGeneratorFactory, registeredBean, null, aotContributions); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, (actual, compiled) -> { SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); assertThat(sourceFile).contains("AotContributedMethod()"); @@ -353,7 +353,7 @@ class BeanDefinitionMethodGeneratorTests { this.methodGeneratorFactory, registeredBean, null, Collections.emptyList()); MethodReference method = generator.generateBeanDefinitionMethod( - this.generationContext, this.beanRegistrationsCode); + this.generationContext, "", this.beanRegistrationsCode); testCompiledResult(method, false, (actual, compiled) -> { DefaultListableBeanFactory freshBeanFactory = new DefaultListableBeanFactory(); freshBeanFactory.registerBeanDefinition("test", actual); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java index 3a39001e33..c1e0a7a3e1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanRegistrationsAotContributionTests.java @@ -37,6 +37,7 @@ import org.springframework.aot.generate.MethodGenerator; import org.springframework.aot.generate.MethodReference; import org.springframework.aot.test.generator.compile.Compiled; import org.springframework.aot.test.generator.compile.TestCompiler; +import org.springframework.aot.test.generator.file.SourceFile; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -67,7 +68,7 @@ class BeanRegistrationsAotContributionTests { private BeanDefinitionMethodGeneratorFactory methodGeneratorFactory; - private final MockBeanFactoryInitializationCode beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(); + private MockBeanFactoryInitializationCode beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(); @BeforeEach void setup() { @@ -98,6 +99,25 @@ class BeanRegistrationsAotContributionTests { }); } + @Test + void applyToWhenHasNameGeneratesPrefixedFeatureName() { + this.beanFactoryInitializationCode = new MockBeanFactoryInitializationCode("Management"); + Map registrations = new LinkedHashMap<>(); + RegisteredBean registeredBean = registerBean( + new RootBeanDefinition(TestBean.class)); + BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator( + this.methodGeneratorFactory, registeredBean, null, + Collections.emptyList()); + registrations.put("testBean", generator); + BeanRegistrationsAotContribution contribution = new BeanRegistrationsAotContribution( + registrations); + contribution.applyTo(this.generationContext, this.beanFactoryInitializationCode); + testCompiledResult((consumer, compiled) -> { + SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions"); + assertThat(sourceFile.getClassName()).endsWith("__ManagementBeanDefinitions"); + }); + } + @Test void applyToCallsRegistrationsWithBeanRegistrationsCode() { List beanRegistrationsCodes = new ArrayList<>(); @@ -110,11 +130,11 @@ class BeanRegistrationsAotContributionTests { @Override MethodReference generateBeanDefinitionMethod( - GenerationContext generationContext, + GenerationContext generationContext, String featureNamePrefix, BeanRegistrationsCode beanRegistrationsCode) { beanRegistrationsCodes.add(beanRegistrationsCode); return super.generateBeanDefinitionMethod(generationContext, - beanRegistrationsCode); + featureNamePrefix, beanRegistrationsCode); } }; @@ -163,6 +183,21 @@ class BeanRegistrationsAotContributionTests { private final List initializers = new ArrayList<>(); + private final String name; + + MockBeanFactoryInitializationCode() { + this(""); + } + + MockBeanFactoryInitializationCode(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + @Override public MethodGenerator getMethodGenerator() { return this.generatedMethods; diff --git a/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextAotGenerator.java b/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextAotGenerator.java index bf02bc3ed0..0ea7827427 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextAotGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextAotGenerator.java @@ -49,7 +49,7 @@ public class ApplicationContextAotGenerator { GenerationContext generationContext, ClassName generatedInitializerClassName) { - generateApplicationContext(applicationContext, null, generationContext, + generateApplicationContext(applicationContext, null, null, generationContext, generatedInitializerClassName); } @@ -58,20 +58,21 @@ public class ApplicationContextAotGenerator { * necessary code to restore the state of its {@link BeanFactory}, using the * specified {@link GenerationContext}. * @param applicationContext the application context to handle - * @param target the target class for the generated initializer + * @param target the target class for the generated initializer (used when generating class names) + * @param name the name of the application context (used when generating class names) * @param generationContext the generation context to use * @param generatedInitializerClassName the class name to use for the * generated application context initializer */ public void generateApplicationContext(GenericApplicationContext applicationContext, - @Nullable Class target, GenerationContext generationContext, + @Nullable Class target, @Nullable String name, GenerationContext generationContext, ClassName generatedInitializerClassName) { applicationContext.refreshForAotProcessing(); DefaultListableBeanFactory beanFactory = applicationContext .getDefaultListableBeanFactory(); ApplicationContextInitializationCodeGenerator codeGenerator = new ApplicationContextInitializationCodeGenerator( - target, applicationContext.getId()); + target, name); new BeanFactoryInitializationAotContributions(beanFactory).applyTo(generationContext, codeGenerator); JavaFile javaFile = codeGenerator.generateJavaFile(generatedInitializerClassName); diff --git a/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextInitializationCodeGenerator.java b/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextInitializationCodeGenerator.java index 051929f492..543e6cb6a8 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextInitializationCodeGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/aot/ApplicationContextInitializationCodeGenerator.java @@ -35,6 +35,7 @@ import org.springframework.javapoet.JavaFile; import org.springframework.javapoet.MethodSpec; import org.springframework.javapoet.ParameterizedTypeName; import org.springframework.javapoet.TypeSpec; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -51,16 +52,16 @@ class ApplicationContextInitializationCodeGenerator private final Class target; - private final String id; + private final String name; private final GeneratedMethods generatedMethods = new GeneratedMethods(); private final List initializers = new ArrayList<>(); - ApplicationContextInitializationCodeGenerator(Class target, String id) { + ApplicationContextInitializationCodeGenerator(Class target, @Nullable String name) { this.target=target; - this.id = (!StringUtils.hasText(id)) ? "" : id; + this.name = (!StringUtils.hasText(name)) ? "" : name; } @@ -70,8 +71,8 @@ class ApplicationContextInitializationCodeGenerator } @Override - public String getId() { - return this.id; + public String getName() { + return this.name; } @Override