Complete refactor of AOT concepts

Remove the AOT code that now has an alternative API.

Closes gh-28414
This commit is contained in:
Phillip Webb
2022-05-04 20:23:24 -07:00
parent 702207d9ee
commit 16e7f1f212
83 changed files with 9 additions and 10950 deletions

View File

@@ -51,9 +51,6 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor;
import org.springframework.beans.factory.generator.BeanFactoryContribution;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
import org.springframework.beans.factory.parsing.ProblemReporter;
@@ -81,7 +78,6 @@ import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.CodeBlock.Builder;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import org.springframework.lang.Nullable;
@@ -108,8 +104,8 @@ import org.springframework.util.ClassUtils;
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
AotContributingBeanFactoryPostProcessor, BeanFactoryInitializationAotProcessor, PriorityOrdered,
ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
BeanFactoryInitializationAotProcessor, PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware,
BeanClassLoaderAware, EnvironmentAware {
/**
* A {@code BeanNameGenerator} using fully qualified class names as default bean names.
@@ -289,15 +285,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
@Override
public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME)
? new ImportAwareBeanFactoryConfiguration(beanFactory) : null);
}
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
return (beanFactory.containsBean(IMPORT_REGISTRY_BEAN_NAME)
? new AotContribution(beanFactory) : null);
}
@@ -518,56 +509,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
}
private static final class ImportAwareBeanFactoryConfiguration implements BeanFactoryContribution {
private final ConfigurableListableBeanFactory beanFactory;
private ImportAwareBeanFactoryConfiguration(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void applyTo(BeanFactoryInitialization initialization) {
Map<String, String> mappings = buildImportAwareMappings();
if (!mappings.isEmpty()) {
MethodSpec method = initialization.generatedTypeContext().getMainGeneratedType()
.addMethod(beanPostProcessorMethod(mappings));
initialization.contribute(code -> code.addStatement("beanFactory.addBeanPostProcessor($N())", method));
ResourceHints resourceHints = initialization.generatedTypeContext().runtimeHints().resources();
mappings.forEach((target, importedFrom) -> resourceHints.registerType(
TypeReference.of(importedFrom)));
}
}
private MethodSpec.Builder beanPostProcessorMethod(Map<String, String> mappings) {
Builder code = CodeBlock.builder();
code.addStatement("$T mappings = new $T<>()", ParameterizedTypeName.get(
Map.class, String.class, String.class), HashMap.class);
mappings.forEach((key, value) -> code.addStatement("mappings.put($S, $S)", key, value));
code.addStatement("return new $T($L)", ImportAwareAotBeanPostProcessor.class, "mappings");
return MethodSpec.methodBuilder("createImportAwareBeanPostProcessor")
.returns(ImportAwareAotBeanPostProcessor.class)
.addModifiers(Modifier.PRIVATE).addCode(code.build());
}
private Map<String, String> buildImportAwareMappings() {
ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
Map<String, String> mappings = new LinkedHashMap<>();
for (String name : this.beanFactory.getBeanDefinitionNames()) {
Class<?> beanType = this.beanFactory.getType(name);
if (beanType != null && ImportAware.class.isAssignableFrom(beanType)) {
String type = ClassUtils.getUserClass(beanType).getName();
AnnotationMetadata importingClassMetadata = ir.getImportingClassFor(type);
if (importingClassMetadata != null) {
mappings.put(type, importingClassMetadata.getClassName());
}
}
}
return mappings;
}
}
private class AotContribution implements BeanFactoryInitializationAotContribution {

View File

@@ -26,7 +26,7 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
/**
* Indicates that one or more {@link RuntimeHintsRegistrar} implementations should be processed.
* <p>Unlike declaring {@link RuntimeHintsRegistrar} as {@code spring.factories},
* <p>Unlike declaring {@link RuntimeHintsRegistrar} as {@code spring/aot.factories},
* {@code @ImportRuntimeHints} allows for more flexible use cases where registrations are only
* processed if the annotated configuration class or bean method is considered by the
* application context.

View File

@@ -1,175 +0,0 @@
/*
* 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.generator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.generator.GeneratedType;
import org.springframework.aot.generator.GeneratedTypeContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor;
import org.springframework.beans.factory.generator.AotContributingBeanPostProcessor;
import org.springframework.beans.factory.generator.BeanDefinitionsContribution;
import org.springframework.beans.factory.generator.BeanFactoryContribution;
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.core.OrderComparator;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
/**
* 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
* @since 6.0
*/
public class ApplicationContextAotGenerator {
private static final Log logger = LogFactory.getLog(ApplicationContextAotGenerator.class);
/**
* Refresh the specified {@link GenericApplicationContext} and generate the
* necessary code to restore the state of its {@link BeanFactory}, using the
* specified {@link GeneratedTypeContext}.
* @param applicationContext the application context to handle
* @param generationContext the generation context to use
*/
public void generateApplicationContext(GenericApplicationContext applicationContext,
GeneratedTypeContext generationContext) {
applicationContext.refreshForAotProcessing();
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
List<BeanFactoryContribution> contributions = resolveBeanFactoryContributions(beanFactory);
filterBeanFactory(contributions, beanFactory);
ApplicationContextInitialization applicationContextInitialization = new ApplicationContextInitialization(generationContext);
applyContributions(contributions, applicationContextInitialization);
GeneratedType mainGeneratedType = generationContext.getMainGeneratedType();
mainGeneratedType.customizeType(type -> type.addSuperinterface(ParameterizedTypeName.get(
ApplicationContextInitializer.class, GenericApplicationContext.class)));
mainGeneratedType.addMethod(initializeMethod(applicationContextInitialization.toCodeBlock()));
}
private MethodSpec.Builder initializeMethod(CodeBlock methodBody) {
MethodSpec.Builder method = MethodSpec.methodBuilder("initialize").addModifiers(Modifier.PUBLIC)
.addParameter(GenericApplicationContext.class, "context").addAnnotation(Override.class);
method.addCode(methodBody);
return method;
}
private void filterBeanFactory(List<BeanFactoryContribution> contributions, DefaultListableBeanFactory beanFactory) {
BiPredicate<String, BeanDefinition> filter = Stream.concat(Stream.of(aotContributingExcludeFilter()),
contributions.stream().map(BeanFactoryContribution::getBeanDefinitionExcludeFilter))
.filter(Objects::nonNull).reduce((n, d) -> false, BiPredicate::or);
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
if (filter.test(beanName, bd)) {
if (logger.isDebugEnabled()) {
logger.debug("Filtering out bean with name" + beanName + ": " + bd);
}
beanFactory.removeBeanDefinition(beanName);
}
}
}
// TODO: is this right?
private BiPredicate<String, BeanDefinition> aotContributingExcludeFilter() {
return (beanName, beanDefinition) -> {
Class<?> type = beanDefinition.getResolvableType().toClass();
return AotContributingBeanFactoryPostProcessor.class.isAssignableFrom(type) ||
AotContributingBeanPostProcessor.class.isAssignableFrom(type);
};
}
private void applyContributions(List<BeanFactoryContribution> contributions,
ApplicationContextInitialization initialization) {
for (BeanFactoryContribution contribution : contributions) {
contribution.applyTo(initialization);
}
}
/**
* Resolve the {@link BeanFactoryContribution} available in the specified
* bean factory. Infrastructure is contributed first, and bean definitions
* registration last.
* @param beanFactory the bean factory to process
* @return the contribution to apply
* @see InfrastructureContribution
* @see BeanDefinitionsContribution
*/
private List<BeanFactoryContribution> resolveBeanFactoryContributions(DefaultListableBeanFactory beanFactory) {
List<BeanFactoryContribution> contributions = new ArrayList<>();
contributions.add(new InfrastructureContribution());
List<AotContributingBeanFactoryPostProcessor> postProcessors = getAotContributingBeanFactoryPostProcessors(beanFactory);
for (AotContributingBeanFactoryPostProcessor postProcessor : postProcessors) {
BeanFactoryContribution contribution = postProcessor.contribute(beanFactory);
if (contribution != null) {
contributions.add(contribution);
}
}
contributions.add(new BeanDefinitionsContribution(beanFactory));
return contributions;
}
private static List<AotContributingBeanFactoryPostProcessor> getAotContributingBeanFactoryPostProcessors(DefaultListableBeanFactory beanFactory) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(AotContributingBeanFactoryPostProcessor.class, true, false);
List<AotContributingBeanFactoryPostProcessor> postProcessors = new ArrayList<>();
for (String ppName : postProcessorNames) {
postProcessors.add(beanFactory.getBean(ppName, AotContributingBeanFactoryPostProcessor.class));
}
postProcessors.addAll(SpringFactoriesLoader.loadFactories(AotContributingBeanFactoryPostProcessor.class,
beanFactory.getBeanClassLoader()));
sortPostProcessors(postProcessors, beanFactory);
return postProcessors;
}
private static void sortPostProcessors(List<?> postProcessors, ConfigurableListableBeanFactory beanFactory) {
// Nothing to sort?
if (postProcessors.size() <= 1) {
return;
}
Comparator<Object> comparatorToUse = null;
if (beanFactory instanceof DefaultListableBeanFactory) {
comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator();
}
if (comparatorToUse == null) {
comparatorToUse = OrderComparator.INSTANCE;
}
postProcessors.sort(comparatorToUse);
}
}

View File

@@ -1,35 +0,0 @@
/*
* 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.generator;
import org.springframework.aot.generator.GeneratedTypeContext;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.context.ApplicationContext;
/**
* The initialization of an {@link ApplicationContext}.
*
* @author Andy Wilkinson
* @since 6.0
*/
public class ApplicationContextInitialization extends BeanFactoryInitialization {
public ApplicationContextInitialization(GeneratedTypeContext generatedTypeContext) {
super(generatedTypeContext);
}
}

View File

@@ -1,43 +0,0 @@
/*
* 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.generator;
import org.springframework.beans.factory.generator.BeanFactoryContribution;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
/**
* A {@link BeanFactoryContribution} that configures the low-level
* infrastructure necessary to process an AOT context.
*
* @author Stephane Nicoll
*/
class InfrastructureContribution implements BeanFactoryContribution {
@Override
public void applyTo(BeanFactoryInitialization initialization) {
initialization.contribute(code -> {
code.add("// infrastructure\n");
code.addStatement("$T beanFactory = context.getDefaultListableBeanFactory()",
DefaultListableBeanFactory.class);
code.addStatement("beanFactory.setAutowireCandidateResolver(new $T())",
ContextAnnotationAutowireCandidateResolver.class);
});
}
}

View File

@@ -1,92 +0,0 @@
/*
* 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.generator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor;
import org.springframework.beans.factory.generator.BeanFactoryContribution;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.core.io.support.SpringFactoriesLoader;
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 RuntimeHintsPostProcessor implements AotContributingBeanFactoryPostProcessor {
private static final Log logger = LogFactory.getLog(RuntimeHintsPostProcessor.class);
@Override
public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
ClassLoader beanClassLoader = beanFactory.getBeanClassLoader();
List<RuntimeHintsRegistrar> registrars =
new ArrayList<>(SpringFactoriesLoader.loadFactories(RuntimeHintsRegistrar.class, beanClassLoader));
Arrays.stream(beanFactory.getBeanNamesForAnnotation(ImportRuntimeHints.class)).forEach(beanDefinitionName -> {
ImportRuntimeHints importRuntimeHints = beanFactory.findAnnotationOnBean(beanDefinitionName, ImportRuntimeHints.class);
if (importRuntimeHints != null) {
Class<? extends RuntimeHintsRegistrar>[] registrarClasses = importRuntimeHints.value();
for (Class<? extends RuntimeHintsRegistrar> registrarClass : registrarClasses) {
logger.trace(LogMessage.format("Loaded [%s] registrar from annotated bean [%s]", registrarClass.getCanonicalName(), beanDefinitionName));
RuntimeHintsRegistrar registrar = BeanUtils.instantiateClass(registrarClass);
registrars.add(registrar);
}
}
});
return new RuntimeHintsRegistrarContribution(registrars, beanClassLoader);
}
static class RuntimeHintsRegistrarContribution implements BeanFactoryContribution {
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(BeanFactoryInitialization initialization) {
this.registrars.forEach(registrar -> {
logger.trace(LogMessage.format("Processing RuntimeHints contribution from [%s]", registrar.getClass().getCanonicalName()));
registrar.registerHints(initialization.generatedTypeContext().runtimeHints(), this.beanClassLoader);
});
}
}
}

View File

@@ -1,10 +0,0 @@
/**
* Support for generating code that represents the state of an application
* context.
*/
@NonNullApi
@NonNullFields
package org.springframework.context.generator;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;