Migrate AOT tests to use GeneratedClasses and refine/polish AOT APIs

Migrate all AOT tests to make use of `GeneratedClasses` rather than
directly generating Java files. This commit also refines and polishes
AOT APIs to being greater consistency.

Specifically:

	- The `MethodGenerator` interface has been removed in favor of
	  working directly with `GeneratedMethods`.
	- The visibility of several constructors and methods has been
	  reduced to package-private.
	- The `using(...)` and `builder` methods have been removed in
	  favor of setting the `Consumer` callbacks directly as
	  constructor arguments.
	- Variable names for builders are now named `type` or `method`
	  depending on what they're building.

Closes gh-28831
This commit is contained in:
Phillip Webb
2022-06-23 13:48:38 -07:00
parent 4f8516e2c3
commit f2d31b7a20
51 changed files with 1255 additions and 1399 deletions

View File

@@ -30,7 +30,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
@@ -42,8 +41,9 @@ import jakarta.persistence.SynchronizationType;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.GeneratedMethod;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodGenerator;
import org.springframework.aot.generate.MethodName;
import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.beans.BeanUtils;
@@ -72,7 +72,6 @@ import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.MethodSpec.Builder;
import org.springframework.jndi.JndiLocatorDelegate;
import org.springframework.jndi.JndiTemplate;
import org.springframework.lang.Nullable;
@@ -766,8 +765,6 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
private static class AotContribution implements BeanRegistrationAotContribution {
private static final String APPLY_METHOD = "apply";
private static final String REGISTERED_BEAN_PARAMETER = "registeredBean";
private static final String INSTANCE_PARAMETER = "instance";
@@ -788,45 +785,38 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
public void applyTo(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode) {
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
.forFeatureComponent("PersistenceInjection", this.target).generate(type -> {
.addForFeatureComponent("PersistenceInjection", this.target, type -> {
type.addJavadoc("Persistence injection for {@link $T}.", this.target);
type.addModifiers(javax.lang.model.element.Modifier.PUBLIC);
});
generatedClass.getMethodGenerator().generateMethod(APPLY_METHOD)
.using(generateMethod(generationContext.getRuntimeHints(), generatedClass.getMethodGenerator()));
beanRegistrationCode.addInstancePostProcessor(
MethodReference.ofStatic(generatedClass.getName(), APPLY_METHOD));
}
private Consumer<Builder> generateMethod(RuntimeHints hints, MethodGenerator methodGenerator) {
return method -> {
GeneratedMethod generatedMethod = generatedClass.getMethods().add("apply", method -> {
method.addJavadoc("Apply the persistence injection.");
method.addModifiers(javax.lang.model.element.Modifier.PUBLIC,
javax.lang.model.element.Modifier.STATIC);
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
method.addParameter(this.target, INSTANCE_PARAMETER);
method.returns(this.target);
method.addCode(generateMethodCode(hints, methodGenerator));
};
method.addCode(generateMethodCode(generationContext.getRuntimeHints(), generatedClass.getMethods()));
});
beanRegistrationCode.addInstancePostProcessor(MethodReference
.ofStatic(generatedClass.getName(), generatedMethod.getName()));
}
private CodeBlock generateMethodCode(RuntimeHints hints,
MethodGenerator methodGenerator) {
CodeBlock.Builder builder = CodeBlock.builder();
InjectionCodeGenerator injectionCodeGenerator = new InjectionCodeGenerator(
hints);
private CodeBlock generateMethodCode(RuntimeHints hints, GeneratedMethods generatedMethods) {
CodeBlock.Builder code = CodeBlock.builder();
InjectionCodeGenerator injectionCodeGenerator = new InjectionCodeGenerator(hints);
for (InjectedElement injectedElement : this.injectedElements) {
CodeBlock resourceToInject = getResourceToInject(methodGenerator,
CodeBlock resourceToInject = generateResourceToInjectCode(generatedMethods,
(PersistenceElement) injectedElement);
builder.add(injectionCodeGenerator.generateInjectionCode(
code.add(injectionCodeGenerator.generateInjectionCode(
injectedElement.getMember(), INSTANCE_PARAMETER,
resourceToInject));
}
builder.addStatement("return $L", INSTANCE_PARAMETER);
return builder.build();
code.addStatement("return $L", INSTANCE_PARAMETER);
return code.build();
}
private CodeBlock getResourceToInject(MethodGenerator methodGenerator,
private CodeBlock generateResourceToInjectCode(GeneratedMethods generatedMethods,
PersistenceElement injectedElement) {
String unitName = injectedElement.unitName;
boolean requireEntityManager = (injectedElement.type != null);
@@ -836,40 +826,38 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
EntityManagerFactoryUtils.class, ListableBeanFactory.class,
REGISTERED_BEAN_PARAMETER, unitName);
}
GeneratedMethod getEntityManagerMethod = methodGenerator
.generateMethod("get", unitName, "EntityManager")
.using(builder -> buildGetEntityManagerMethod(builder,
injectedElement));
return CodeBlock.of("$L($L)", getEntityManagerMethod.getName(),
REGISTERED_BEAN_PARAMETER);
GeneratedMethod generatedMethod = generatedMethods
.add(MethodName.of("get", unitName, "EntityManager"), method ->
generateGetEntityManagerMethod(method, injectedElement));
return CodeBlock.of("$L($L)", generatedMethod.getName(), REGISTERED_BEAN_PARAMETER);
}
private void buildGetEntityManagerMethod(MethodSpec.Builder builder,
private void generateGetEntityManagerMethod(MethodSpec.Builder method,
PersistenceElement injectedElement) {
String unitName = injectedElement.unitName;
Properties properties = injectedElement.properties;
builder.addJavadoc("Get the '$L' {@link $T}",
method.addJavadoc("Get the '$L' {@link $T}",
(StringUtils.hasLength(unitName)) ? unitName : "default",
EntityManager.class);
builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC,
method.addModifiers(javax.lang.model.element.Modifier.PUBLIC,
javax.lang.model.element.Modifier.STATIC);
builder.returns(EntityManager.class);
builder.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
builder.addStatement(
method.returns(EntityManager.class);
method.addParameter(RegisteredBean.class, REGISTERED_BEAN_PARAMETER);
method.addStatement(
"$T entityManagerFactory = $T.findEntityManagerFactory(($T) $L.getBeanFactory(), $S)",
EntityManagerFactory.class, EntityManagerFactoryUtils.class,
ListableBeanFactory.class, REGISTERED_BEAN_PARAMETER, unitName);
boolean hasProperties = !CollectionUtils.isEmpty(properties);
if (hasProperties) {
builder.addStatement("$T properties = new Properties()",
method.addStatement("$T properties = new Properties()",
Properties.class);
for (String propertyName : new TreeSet<>(
properties.stringPropertyNames())) {
builder.addStatement("properties.put($S, $S)", propertyName,
method.addStatement("properties.put($S, $S)", propertyName,
properties.getProperty(propertyName));
}
}
builder.addStatement(
method.addStatement(
"return $T.createSharedEntityManager(entityManagerFactory, $L, $L)",
SharedEntityManagerCreator.class,
(hasProperties) ? "properties" : null,