Add updated ApplicationContextAotGenerator
Add `ApplicationContextAotGenerator` implementation that makes use of the new AOT generation APIs. See gh-28414
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
|
||||
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.support.GenericApplicationContext;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.JavaFile;
|
||||
|
||||
/**
|
||||
* Process an {@link ApplicationContext} and its {@link BeanFactory} to generate
|
||||
* code that represents the state of the bean factory, as well as the necessary
|
||||
* hints that can be used at runtime in a constrained environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
public class ApplicationContextAotGenerator {
|
||||
|
||||
/**
|
||||
* 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 generationContext the generation context to use
|
||||
* @param generatedInitializerClassName the class name to use for the
|
||||
* generated application context initializer
|
||||
*/
|
||||
public void generateApplicationContext(GenericApplicationContext applicationContext,
|
||||
GenerationContext generationContext,
|
||||
ClassName generatedInitializerClassName) {
|
||||
|
||||
applicationContext.refreshForAotProcessing();
|
||||
DefaultListableBeanFactory beanFactory = applicationContext
|
||||
.getDefaultListableBeanFactory();
|
||||
ApplicationContextInitializationCodeGenerator codeGenerator = new ApplicationContextInitializationCodeGenerator();
|
||||
new BeanFactoryInitializationContributions(beanFactory).applyTo(generationContext,
|
||||
codeGenerator);
|
||||
JavaFile javaFile = codeGenerator.generateJavaFile(generatedInitializerClassName);
|
||||
generationContext.getGeneratedFiles().addSourceFile(javaFile);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedMethods;
|
||||
import org.springframework.aot.generate.MethodGenerator;
|
||||
import org.springframework.aot.generate.MethodReference;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Internal code generator to create the application context initializer.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class ApplicationContextInitializationCodeGenerator
|
||||
implements BeanFactoryInitializationCode {
|
||||
|
||||
private static final String APPLICATION_CONTEXT_VARIABLE = "applicationContext";
|
||||
|
||||
|
||||
private final GeneratedMethods generatedMethods = new GeneratedMethods();
|
||||
|
||||
private final List<MethodReference> initializers = new ArrayList<>();
|
||||
|
||||
|
||||
@Override
|
||||
public MethodGenerator getMethodGenerator() {
|
||||
return this.generatedMethods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitializer(MethodReference methodReference) {
|
||||
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();
|
||||
}
|
||||
|
||||
private MethodSpec generateInitializeMethod() {
|
||||
MethodSpec.Builder builder = MethodSpec.methodBuilder("initialize");
|
||||
builder.addAnnotation(Override.class);
|
||||
builder.addModifiers(Modifier.PUBLIC);
|
||||
builder.addParameter(GenericApplicationContext.class,
|
||||
APPLICATION_CONTEXT_VARIABLE);
|
||||
builder.addCode(generateInitializeCode());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private CodeBlock generateInitializeCode() {
|
||||
CodeBlock.Builder builder = CodeBlock.builder();
|
||||
builder.addStatement("$T $L = $L.getDefaultListableBeanFactory()",
|
||||
DefaultListableBeanFactory.class, BEAN_FACTORY_VARIABLE,
|
||||
APPLICATION_CONTEXT_VARIABLE);
|
||||
builder.addStatement("$L.setAutowireCandidateResolver(new $T())",
|
||||
BEAN_FACTORY_VARIABLE, ContextAnnotationAutowireCandidateResolver.class);
|
||||
for (MethodReference initializer : this.initializers) {
|
||||
builder.addStatement(
|
||||
initializer.toInvokeCodeBlock(CodeBlock.of(BEAN_FACTORY_VARIABLE)));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.beans.factory.aot.AotFactoriesLoader;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
|
||||
/**
|
||||
* A collection of {@link BeanFactoryInitializationAotContribution AOT
|
||||
* contributions} obtained from {@link BeanFactoryInitializationAotProcessor AOT
|
||||
* processors}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 6.0
|
||||
*/
|
||||
class BeanFactoryInitializationContributions {
|
||||
|
||||
private final List<BeanFactoryInitializationAotContribution> contributions;
|
||||
|
||||
|
||||
BeanFactoryInitializationContributions(DefaultListableBeanFactory beanFactory) {
|
||||
this(beanFactory, new AotFactoriesLoader(beanFactory));
|
||||
}
|
||||
|
||||
BeanFactoryInitializationContributions(DefaultListableBeanFactory beanFactory,
|
||||
AotFactoriesLoader loader) {
|
||||
this.contributions = getContributions(beanFactory, getProcessors(loader));
|
||||
}
|
||||
|
||||
|
||||
private List<BeanFactoryInitializationAotProcessor> getProcessors(
|
||||
AotFactoriesLoader loader) {
|
||||
List<BeanFactoryInitializationAotProcessor> processors = new ArrayList<>(
|
||||
loader.load(BeanFactoryInitializationAotProcessor.class));
|
||||
processors.add(new RuntimeHintsBeanFactoryInitializationAotProcessor());
|
||||
return Collections.unmodifiableList(processors);
|
||||
}
|
||||
|
||||
private List<BeanFactoryInitializationAotContribution> getContributions(
|
||||
DefaultListableBeanFactory beanFactory,
|
||||
List<BeanFactoryInitializationAotProcessor> processors) {
|
||||
List<BeanFactoryInitializationAotContribution> contributions = new ArrayList<>();
|
||||
for (BeanFactoryInitializationAotProcessor processor : processors) {
|
||||
BeanFactoryInitializationAotContribution contribution = processor
|
||||
.processAheadOfTime(beanFactory);
|
||||
if (contribution != null) {
|
||||
contributions.add(contribution);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(contributions);
|
||||
}
|
||||
|
||||
void applyTo(GenerationContext generationContext,
|
||||
BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
for (BeanFactoryInitializationAotContribution contribution : this.contributions) {
|
||||
contribution.applyTo(generationContext, beanFactoryInitializationCode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.aot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.aot.AotFactoriesLoader;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.ImportRuntimeHints;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* AOT {@code BeanFactoryPostProcessor} that processes
|
||||
* {@link RuntimeHintsRegistrar} implementations declared as
|
||||
* {@code spring.factories} or using
|
||||
* {@link ImportRuntimeHints @ImportRuntimeHints} annotated configuration
|
||||
* classes or bean methods.
|
||||
* <p>
|
||||
* This processor is registered by default in the
|
||||
* {@link ApplicationContextAotGenerator} as it is only useful in an AOT
|
||||
* context.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @see ApplicationContextAotGenerator
|
||||
*/
|
||||
class RuntimeHintsBeanFactoryInitializationAotProcessor
|
||||
implements BeanFactoryInitializationAotProcessor {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(RuntimeHintsBeanFactoryInitializationAotProcessor.class);
|
||||
|
||||
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
AotFactoriesLoader loader = new AotFactoriesLoader(beanFactory);
|
||||
List<RuntimeHintsRegistrar> registrars = new ArrayList<>(
|
||||
loader.load(RuntimeHintsRegistrar.class));
|
||||
for (String beanName : beanFactory
|
||||
.getBeanNamesForAnnotation(ImportRuntimeHints.class)) {
|
||||
ImportRuntimeHints annotation = beanFactory.findAnnotationOnBean(beanName,
|
||||
ImportRuntimeHints.class);
|
||||
if (annotation != null) {
|
||||
registrars.addAll(extracted(beanName, annotation));
|
||||
}
|
||||
}
|
||||
return new RuntimeHintsRegistrarContribution(registrars,
|
||||
beanFactory.getBeanClassLoader());
|
||||
}
|
||||
|
||||
private List<RuntimeHintsRegistrar> extracted(String beanName,
|
||||
ImportRuntimeHints annotation) {
|
||||
Class<? extends RuntimeHintsRegistrar>[] registrarClasses = annotation.value();
|
||||
List<RuntimeHintsRegistrar> registrars = new ArrayList<>(registrarClasses.length);
|
||||
for (Class<? extends RuntimeHintsRegistrar> registrarClass : registrarClasses) {
|
||||
logger.trace(
|
||||
LogMessage.format("Loaded [%s] registrar from annotated bean [%s]",
|
||||
registrarClass.getCanonicalName(), beanName));
|
||||
registrars.add(BeanUtils.instantiateClass(registrarClass));
|
||||
}
|
||||
return registrars;
|
||||
}
|
||||
|
||||
|
||||
static class RuntimeHintsRegistrarContribution
|
||||
implements BeanFactoryInitializationAotContribution {
|
||||
|
||||
|
||||
private final List<RuntimeHintsRegistrar> registrars;
|
||||
|
||||
@Nullable
|
||||
private final ClassLoader beanClassLoader;
|
||||
|
||||
|
||||
RuntimeHintsRegistrarContribution(List<RuntimeHintsRegistrar> registrars,
|
||||
@Nullable ClassLoader beanClassLoader) {
|
||||
this.registrars = registrars;
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext,
|
||||
BeanFactoryInitializationCode beanFactoryInitializationCode) {
|
||||
RuntimeHints hints = generationContext.getRuntimeHints();
|
||||
this.registrars.forEach(registrar -> {
|
||||
logger.trace(LogMessage.format(
|
||||
"Processing RuntimeHints contribution from [%s]",
|
||||
registrar.getClass().getCanonicalName()));
|
||||
registrar.registerHints(hints, this.beanClassLoader);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* AOT support for application contexts.
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.context.aot;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
Reference in New Issue
Block a user