Harmonize generated class name conventions

This commit moves the responsibility of naming classes to the
GenerationContext. This was already largely the case before, except that
the concept of a "mainTarget" and "featureNamePrefix" was specific
to bean factory initialization contributors.

ClassNameGenerator should now be instantiated with a default target
and an optional feature name prefix. As a result, it does no longer
generate class names in the "__" package.

GeneratedClasses can now provide a new, unique, GeneratedClass or
offer a container for retrieving the same GeneratedClass based on an
identifier. This lets all contributors use this facility rather than
creating JavaFile manually. This also means that ClassNameGenerator
is no longer exposed.

Because the naming conventions are now part of the GenerationContext, it
is required to be able to retrieve a specialized version of it if a
code generation round needs to use different naming conventions. A new
withName method has been added to that effect.

Closes gh-28585
This commit is contained in:
Stephane Nicoll
2022-06-22 14:20:00 +02:00
parent b121eed753
commit 6199835d6e
31 changed files with 652 additions and 565 deletions

View File

@@ -16,14 +16,14 @@
package org.springframework.context.aot;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.JavaFile;
import org.springframework.lang.Nullable;
/**
* Process an {@link ApplicationContext} and its {@link BeanFactory} to generate
@@ -42,41 +42,20 @@ public class ApplicationContextAotGenerator {
* specified {@link GenerationContext}.
* @param applicationContext the application context to handle
* @param generationContext the generation context to use
* @param generatedInitializerClassName the class name to use for the
* generated application context initializer
* @return the class name of the {@link ApplicationContextInitializer} entry point
*/
public void generateApplicationContext(GenericApplicationContext applicationContext,
GenerationContext generationContext,
ClassName generatedInitializerClassName) {
generateApplicationContext(applicationContext, null, null, generationContext,
generatedInitializerClassName);
}
/**
* Refresh the specified {@link GenericApplicationContext} and generate the
* 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 (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, @Nullable String name, GenerationContext generationContext,
ClassName generatedInitializerClassName) {
public ClassName generateApplicationContext(GenericApplicationContext applicationContext,
GenerationContext generationContext) {
applicationContext.refreshForAotProcessing();
DefaultListableBeanFactory beanFactory = applicationContext
.getDefaultListableBeanFactory();
ApplicationContextInitializationCodeGenerator codeGenerator = new ApplicationContextInitializationCodeGenerator(
target, name);
ApplicationContextInitializationCodeGenerator codeGenerator = new ApplicationContextInitializationCodeGenerator();
new BeanFactoryInitializationAotContributions(beanFactory).applyTo(generationContext,
codeGenerator);
JavaFile javaFile = codeGenerator.generateJavaFile(generatedInitializerClassName);
generationContext.getGeneratedFiles().addSourceFile(javaFile);
GeneratedClass applicationContextInitializer = generationContext.getGeneratedClasses()
.forFeature("ApplicationContextInitializer")
.generate(codeGenerator.generateJavaFile());
return applicationContextInitializer.getName();
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.context.aot;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javax.lang.model.element.Modifier;
@@ -29,14 +30,10 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
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;
/**
* Internal code generator to create the application context initializer.
@@ -50,33 +47,11 @@ class ApplicationContextInitializationCodeGenerator
private static final String APPLICATION_CONTEXT_VARIABLE = "applicationContext";
@Nullable
private final Class<?> target;
private final String name;
private final GeneratedMethods generatedMethods = new GeneratedMethods();
private final List<MethodReference> initializers = new ArrayList<>();
ApplicationContextInitializationCodeGenerator(@Nullable Class<?> target, @Nullable String name) {
this.target = target;
this.name = (!StringUtils.hasText(name)) ? "" : name;
}
@Override
@Nullable
public Class<?> getTarget() {
return this.target;
}
@Override
public String getName() {
return this.name;
}
@Override
public MethodGenerator getMethodGenerator() {
return this.generatedMethods;
@@ -87,17 +62,17 @@ class ApplicationContextInitializationCodeGenerator
this.initializers.add(methodReference);
}
JavaFile generateJavaFile(ClassName className) {
TypeSpec.Builder builder = TypeSpec.classBuilder(className);
builder.addJavadoc(
"{@link $T} to restore an application context based on previous AOT processing.",
ApplicationContextInitializer.class);
builder.addModifiers(Modifier.PUBLIC);
builder.addSuperinterface(ParameterizedTypeName.get(
ApplicationContextInitializer.class, GenericApplicationContext.class));
builder.addMethod(generateInitializeMethod());
this.generatedMethods.doWithMethodSpecs(builder::addMethod);
return JavaFile.builder(className.packageName(), builder.build()).build();
Consumer<TypeSpec.Builder> generateJavaFile() {
return builder -> {
builder.addJavadoc(
"{@link $T} to restore an application context based on previous AOT processing.",
ApplicationContextInitializer.class);
builder.addModifiers(Modifier.PUBLIC);
builder.addSuperinterface(ParameterizedTypeName.get(
ApplicationContextInitializer.class, GenericApplicationContext.class));
builder.addMethod(generateInitializeMethod());
this.generatedMethods.doWithMethodSpecs(builder::addMethod);
};
}
private MethodSpec generateInitializeMethod() {