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;

View File

@@ -1,115 +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.annotation;
import java.io.IOException;
import java.io.StringWriter;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
import org.springframework.aot.generator.GeneratedType;
import org.springframework.aot.generator.GeneratedTypeContext;
import org.springframework.beans.factory.generator.BeanFactoryContribution;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
import org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.support.CodeSnippet;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@code ImportAwareBeanFactoryConfiguration}.
*
* @author Stephane Nicoll
*/
public class ImportAwareBeanFactoryContributionTests {
@Test
void contributeWithImportAwareConfigurationRegistersBeanPostProcessor() {
BeanFactoryContribution contribution = createContribution(ImportConfiguration.class);
assertThat(contribution).isNotNull();
BeanFactoryInitialization initialization = new BeanFactoryInitialization(createGenerationContext());
contribution.applyTo(initialization);
assertThat(CodeSnippet.of(initialization.toCodeBlock()).getSnippet()).isEqualTo("""
beanFactory.addBeanPostProcessor(createImportAwareBeanPostProcessor());
""");
}
@Test
void contributeWithImportAwareConfigurationCreateMappingsMethod() {
BeanFactoryContribution contribution = createContribution(ImportConfiguration.class);
assertThat(contribution).isNotNull();
GeneratedTypeContext generationContext = createGenerationContext();
contribution.applyTo(new BeanFactoryInitialization(generationContext));
assertThat(codeOf(generationContext.getMainGeneratedType())).contains("""
private ImportAwareAotBeanPostProcessor createImportAwareBeanPostProcessor() {
Map<String, String> mappings = new HashMap<>();
mappings.put("org.springframework.context.testfixture.context.generator.annotation.ImportAwareConfiguration", "org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration");
return new ImportAwareAotBeanPostProcessor(mappings);
}
""");
}
@Test
void contributeWithImportAwareConfigurationRegisterBytecodeResourceHint() {
BeanFactoryContribution contribution = createContribution(ImportConfiguration.class);
assertThat(contribution).isNotNull();
GeneratedTypeContext generationContext = createGenerationContext();
contribution.applyTo(new BeanFactoryInitialization(generationContext));
assertThat(generationContext.runtimeHints().resources().resourcePatterns())
.singleElement().satisfies(resourceHint -> assertThat(resourceHint.getIncludes()).containsOnly(
"org/springframework/context/testfixture/context/generator/annotation/ImportConfiguration.class"));
}
@Test
void contributeWithNoImportAwareConfigurationReturnsNull() {
assertThat(createContribution(SimpleConfiguration.class)).isNull();
}
@Nullable
private BeanFactoryContribution createContribution(Class<?> type) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("configuration", new RootBeanDefinition(type));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
return pp.contribute(beanFactory);
}
private GeneratedTypeContext createGenerationContext() {
return new DefaultGeneratedTypeContext("com.example", packageName ->
GeneratedType.of(ClassName.get(packageName, "Test")));
}
private String codeOf(GeneratedType type) {
try {
StringWriter out = new StringWriter();
type.toJavaFile().writeTo(out);
return out.toString();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}

View File

@@ -1,410 +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.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.util.Enumeration;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
import org.springframework.aot.generator.GeneratedType;
import org.springframework.aot.generator.GeneratedTypeContext;
import org.springframework.aot.test.generator.compile.TestCompiler;
import org.springframework.aot.test.generator.file.SourceFile;
import org.springframework.aot.test.generator.file.SourceFiles;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
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.BeanFactoryContribution;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.beans.factory.generator.BeanInstantiationContribution;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.testfixture.context.generator.SimpleComponent;
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.JavaFile;
import org.springframework.lang.Nullable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link ApplicationContextAotGenerator}.
*
* @author Stephane Nicoll
*/
class ApplicationContextAotGeneratorTests {
private static final ClassName MAIN_GENERATED_TYPE = ClassName.get("com.example", "Test");
@Test
void generateApplicationContextWithSimpleBean() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("test", new RootBeanDefinition(SimpleComponent.class));
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("test");
assertThat(aotContext.getBean("test")).isInstanceOf(SimpleComponent.class);
}));
}
@Test
void generateApplicationContextWithAutowiring() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
context.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
context.registerBeanDefinition("number", BeanDefinitionBuilder.rootBeanDefinition(Integer.class, "valueOf")
.addConstructorArgValue("42").getBeanDefinition());
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number");
AutowiredComponent bean = aotContext.getBean(AutowiredComponent.class);
assertThat(bean.getEnvironment()).isSameAs(aotContext.getEnvironment());
assertThat(bean.getCounter()).isEqualTo(42);
}));
}
@Test
void generateApplicationContextWithInitDestroyMethods() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
context.registerBeanDefinition("initDestroyComponent", new RootBeanDefinition(InitDestroyComponent.class));
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class);
assertThat(bean.events).containsExactly("init");
aotContext.close();
assertThat(bean.events).containsExactly("init", "destroy");
}));
}
@Test
void generateApplicationContextWithMultipleInitDestroyMethods() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
BeanDefinitionBuilder.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
beanDefinition.setInitMethodName("customInit");
beanDefinition.setDestroyMethodName("customDestroy");
context.registerBeanDefinition("initDestroyComponent", beanDefinition);
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext -> {
assertThat(aotContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
InitDestroyComponent bean = aotContext.getBean(InitDestroyComponent.class);
assertThat(bean.events).containsExactly("customInit", "init");
aotContext.close();
assertThat(bean.events).containsExactly("customInit", "init", "customDestroy", "destroy");
}));
}
@Test
void generateApplicationContextWitNoContributors() {
GeneratedTypeContext generationContext = createGenerationContext();
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
generator.generateApplicationContext(new GenericApplicationContext(), generationContext);
assertThat(write(generationContext.getMainGeneratedType())).contains("""
public class Test implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext context) {
// infrastructure
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
}
}
""");
}
@Test
void generateApplicationContextLoadsBeanFactoryContributors() {
GeneratedTypeContext generationContext = createGenerationContext();
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.setClassLoader(
new TestSpringFactoriesClassLoader("bean-factory-contributors.factories"));
generator.generateApplicationContext(applicationContext, generationContext);
assertThat(write(generationContext.getMainGeneratedType())).contains("""
public class Test implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext context) {
// infrastructure
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// Test
}
}
""");
}
@Test
void generateApplicationContextApplyContributionAsIsWithNewLineAtTheEnd() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
registerAotContributingBeanDefinition(applicationContext, "bpp", code -> code.add("// Hello"));
GeneratedTypeContext generationContext = createGenerationContext();
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
generator.generateApplicationContext(applicationContext, generationContext);
assertThat(write(generationContext.getMainGeneratedType())).contains("""
public class Test implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext context) {
// infrastructure
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// Hello
}
}
""");
}
@Test
void generateApplicationContextApplyMultipleContributionAsIsWithNewLineAtTheEnd() {
GeneratedTypeContext generationContext = createGenerationContext();
GenericApplicationContext applicationContext = new GenericApplicationContext();
registerAotContributingBeanDefinition(applicationContext, "bpp", code -> code.add("// Hello"));
registerAotContributingBeanDefinition(applicationContext, "bpp2", code -> code.add("// World"));
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
generator.generateApplicationContext(applicationContext, generationContext);
assertThat(write(generationContext.getMainGeneratedType())).contains("""
public class Test implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext context) {
// infrastructure
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
// Hello
// World
}
}
""");
}
@Test
void generateApplicationContextExcludeAotContributingBeanFactoryPostProcessorByDefault() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("test", new RootBeanDefinition(NoOpAotContributingBeanFactoryPostProcessor.class));
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext ->
assertThat(aotContext.getBeanDefinitionNames()).isEmpty()));
}
@Test
void generateApplicationContextExcludeAotContributingBeanPostProcessorByDefault() {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBeanDefinition("test", new RootBeanDefinition(NoOpAotContributingBeanPostProcessor.class));
compile(context, toFreshApplicationContext(GenericApplicationContext::new, aotContext ->
assertThat(aotContext.getBeanDefinitionNames()).isEmpty()));
}
@Test
void generateApplicationContextInvokeExcludePredicateInOrder() {
GeneratedTypeContext generationContext = createGenerationContext();
GenericApplicationContext applicationContext = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
BiPredicate<String, BeanDefinition> excludeFilter = mockExcludeFilter();
given(excludeFilter.test(eq("bean1"), any(BeanDefinition.class))).willReturn(Boolean.FALSE);
given(excludeFilter.test(eq("bean2"), any(BeanDefinition.class))).willReturn(Boolean.TRUE);
applicationContext.registerBeanDefinition("bean2", new RootBeanDefinition(SimpleComponent.class));
applicationContext.registerBeanDefinition("bean1", new RootBeanDefinition(SimpleComponent.class));
registerAotContributingBeanDefinition(applicationContext, "bpp", code -> {}, excludeFilter);
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
generator.generateApplicationContext(applicationContext, generationContext);
assertThat(write(generationContext.getMainGeneratedType()))
.doesNotContain("bean2").doesNotContain("bpp")
.contains("BeanDefinitionRegistrar.of(\"bean1\", SimpleComponent.class)");
verify(excludeFilter).test(eq("bean2"), any(BeanDefinition.class));
verify(excludeFilter).test("bean1", beanFactory.getMergedBeanDefinition("bean1"));
}
@SuppressWarnings("unchecked")
private BiPredicate<String, BeanDefinition> mockExcludeFilter() {
return mock(BiPredicate.class);
}
@SuppressWarnings("rawtypes")
private void compile(GenericApplicationContext applicationContext, Consumer<ApplicationContextInitializer> initializer) {
DefaultGeneratedTypeContext generationContext = createGenerationContext();
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
generator.generateApplicationContext(applicationContext, generationContext);
SourceFiles sourceFiles = SourceFiles.none();
for (JavaFile javaFile : generationContext.toJavaFiles()) {
sourceFiles = sourceFiles.and(SourceFile.of((javaFile::writeTo)));
}
TestCompiler.forSystem().withSources(sourceFiles).compile(compiled -> {
ApplicationContextInitializer instance = compiled.getInstance(ApplicationContextInitializer.class, MAIN_GENERATED_TYPE.canonicalName());
initializer.accept(instance);
});
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private <T extends GenericApplicationContext> Consumer<ApplicationContextInitializer> toFreshApplicationContext(
Supplier<T> applicationContextFactory, Consumer<T> context) {
return applicationContextInitializer -> {
T applicationContext = applicationContextFactory.get();
applicationContextInitializer.initialize(applicationContext);
applicationContext.refresh();
context.accept(applicationContext);
};
}
private DefaultGeneratedTypeContext createGenerationContext() {
return new DefaultGeneratedTypeContext(MAIN_GENERATED_TYPE.packageName(), packageName ->
GeneratedType.of(ClassName.get(packageName, MAIN_GENERATED_TYPE.simpleName())));
}
private String write(GeneratedType generatedType) {
try {
StringWriter out = new StringWriter();
generatedType.toJavaFile().writeTo(out);
return out.toString();
}
catch (IOException ex) {
throw new IllegalStateException("Failed to write " + generatedType, ex);
}
}
private void registerAotContributingBeanDefinition(GenericApplicationContext context, String name,
Consumer<CodeBlock.Builder> code) {
registerAotContributingBeanDefinition(context, name, code,
(beanName, beanDefinition) -> name.equals(beanName));
}
private void registerAotContributingBeanDefinition(GenericApplicationContext context, String name,
Consumer<CodeBlock.Builder> code, BiPredicate<String, BeanDefinition> excludeFilter) {
BeanFactoryContribution contribution = new TestBeanFactoryContribution(
initialization -> initialization.contribute(code), excludeFilter);
context.registerBeanDefinition(name, BeanDefinitionBuilder.rootBeanDefinition(
TestAotContributingBeanFactoryPostProcessor.class, () ->
new TestAotContributingBeanFactoryPostProcessor(contribution)).getBeanDefinition());
}
static class TestAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor {
@Nullable
private final BeanFactoryContribution beanFactoryContribution;
TestAotContributingBeanFactoryPostProcessor(@Nullable BeanFactoryContribution beanFactoryContribution) {
this.beanFactoryContribution = beanFactoryContribution;
}
TestAotContributingBeanFactoryPostProcessor() {
this(null);
}
@Override
public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
return this.beanFactoryContribution;
}
}
static class TextAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor {
@Override
public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
return initialization -> initialization.contribute(code -> code.add("// Test\n"));
}
}
static class NoOpAotContributingBeanFactoryPostProcessor implements AotContributingBeanFactoryPostProcessor {
@Override
public BeanFactoryContribution contribute(ConfigurableListableBeanFactory beanFactory) {
return null;
}
}
static class NoOpAotContributingBeanPostProcessor implements AotContributingBeanPostProcessor {
@Override
public BeanInstantiationContribution contribute(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
return null;
}
@Override
public int getOrder() {
return 0;
}
}
static class TestBeanFactoryContribution implements BeanFactoryContribution {
private final Consumer<BeanFactoryInitialization> contribution;
private final BiPredicate<String, BeanDefinition> excludeFilter;
public TestBeanFactoryContribution(Consumer<BeanFactoryInitialization> contribution,
BiPredicate<String, BeanDefinition> excludeFilter) {
this.contribution = contribution;
this.excludeFilter = excludeFilter;
}
@Override
public void applyTo(BeanFactoryInitialization initialization) {
this.contribution.accept(initialization);
}
@Override
public BiPredicate<String, BeanDefinition> getBeanDefinitionExcludeFilter() {
return this.excludeFilter;
}
}
static class TestSpringFactoriesClassLoader extends ClassLoader {
private final String factoriesName;
TestSpringFactoriesClassLoader(String factoriesName) {
super(RuntimeHintsPostProcessorTests.class.getClassLoader());
this.factoriesName = factoriesName;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if ("META-INF/spring.factories".equals(name)) {
return super.getResources("org/springframework/context/generator/aot/" + this.factoriesName);
}
return super.getResources(name);
}
}
}

View File

@@ -1,61 +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.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
import org.springframework.aot.generator.GeneratedType;
import org.springframework.aot.generator.GeneratedTypeContext;
import org.springframework.beans.factory.generator.BeanFactoryInitialization;
import org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver;
import org.springframework.javapoet.ClassName;
import org.springframework.javapoet.support.CodeSnippet;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link InfrastructureContribution}.
*
* @author Stephane Nicoll
*/
class InfrastructureContributionTests {
@Test
void contributeInfrastructure() {
CodeSnippet codeSnippet = contribute(createGenerationContext());
assertThat(codeSnippet.getSnippet()).isEqualTo("""
// infrastructure
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
""");
assertThat(codeSnippet.hasImport(ContextAnnotationAutowireCandidateResolver.class)).isTrue();
}
private CodeSnippet contribute(GeneratedTypeContext generationContext) {
InfrastructureContribution contribution = new InfrastructureContribution();
BeanFactoryInitialization initialization = new BeanFactoryInitialization(generationContext);
contribution.applyTo(initialization);
return CodeSnippet.of(initialization.toCodeBlock());
}
private GeneratedTypeContext createGenerationContext() {
return new DefaultGeneratedTypeContext("com.example", packageName ->
GeneratedType.of(ClassName.get(packageName, "Test")));
}
}

View File

@@ -1,183 +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.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generator.DefaultGeneratedTypeContext;
import org.springframework.aot.generator.GeneratedType;
import org.springframework.aot.hint.ResourceBundleHint;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.ClassName;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link RuntimeHintsPostProcessor}.
*
* @author Brian Clozel
*/
class RuntimeHintsPostProcessorTests {
private DefaultGeneratedTypeContext generationContext;
private ApplicationContextAotGenerator generator;
@BeforeEach
void setup() {
this.generationContext = createGenerationContext();
this.generator = new ApplicationContextAotGenerator();
}
@Test
void shouldProcessRegistrarOnConfiguration() {
GenericApplicationContext applicationContext = createContext(ConfigurationWithHints.class);
this.generator.generateApplicationContext(applicationContext, this.generationContext);
assertThatSampleRegistrarContributed();
}
@Test
void shouldProcessRegistrarOnBeanMethod() {
GenericApplicationContext applicationContext = createContext(ConfigurationWithBeanDeclaringHints.class);
this.generator.generateApplicationContext(applicationContext, this.generationContext);
assertThatSampleRegistrarContributed();
}
@Test
void shouldProcessRegistrarInSpringFactory() {
GenericApplicationContext applicationContext = createContext();
applicationContext.setClassLoader(new TestSpringFactoriesClassLoader("test.factories"));
this.generator.generateApplicationContext(applicationContext, this.generationContext);
assertThatSampleRegistrarContributed();
}
@Test
void shouldRejectRuntimeHintsRegistrarWithoutDefaultConstructor() {
GenericApplicationContext applicationContext = createContext(ConfigurationWithIllegalRegistrar.class);
assertThatThrownBy(() -> this.generator.generateApplicationContext(applicationContext, this.generationContext))
.isInstanceOf(BeanInstantiationException.class);
}
private void assertThatSampleRegistrarContributed() {
Stream<ResourceBundleHint> bundleHints = this.generationContext.runtimeHints().resources().resourceBundles();
assertThat(bundleHints).anyMatch(bundleHint -> "sample".equals(bundleHint.getBaseName()));
}
private GenericApplicationContext createContext(Class<?>... configClasses) {
GenericApplicationContext applicationContext = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(applicationContext);
for (Class<?> configClass : configClasses) {
applicationContext.registerBeanDefinition(configClass.getSimpleName(), new RootBeanDefinition(configClass));
}
applicationContext.registerBeanDefinition("runtimeHintsPostProcessor",
BeanDefinitionBuilder.rootBeanDefinition(RuntimeHintsPostProcessor.class, RuntimeHintsPostProcessor::new).getBeanDefinition());
return applicationContext;
}
private DefaultGeneratedTypeContext createGenerationContext() {
ClassName mainGeneratedType = ClassName.get("com.example", "Test");
return new DefaultGeneratedTypeContext(mainGeneratedType.packageName(), packageName ->
GeneratedType.of(ClassName.get(packageName, mainGeneratedType.simpleName())));
}
@ImportRuntimeHints(SampleRuntimeHintsRegistrar.class)
@Configuration(proxyBeanMethods = false)
static class ConfigurationWithHints {
}
@Configuration(proxyBeanMethods = false)
static class ConfigurationWithBeanDeclaringHints {
@Bean
@ImportRuntimeHints(SampleRuntimeHintsRegistrar.class)
SampleBean sampleBean() {
return new SampleBean();
}
}
public static class SampleRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerResourceBundle("sample");
}
}
static class SampleBean {
}
@ImportRuntimeHints(IllegalRuntimeHintsRegistrar.class)
@Configuration(proxyBeanMethods = false)
static class ConfigurationWithIllegalRegistrar {
}
public static class IllegalRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
public IllegalRuntimeHintsRegistrar(String arg) {
}
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerResourceBundle("sample");
}
}
static class TestSpringFactoriesClassLoader extends ClassLoader {
private final String factoriesName;
TestSpringFactoriesClassLoader(String factoriesName) {
super(RuntimeHintsPostProcessorTests.class.getClassLoader());
this.factoriesName = factoriesName;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
if ("META-INF/spring.factories".equals(name)) {
return super.getResources("org/springframework/context/generator/runtimehints/" + this.factoriesName);
}
return super.getResources(name);
}
}
}

View File

@@ -1,3 +0,0 @@
org.springframework.beans.factory.generator.AotContributingBeanFactoryPostProcessor= \
org.springframework.context.generator.ApplicationContextAotGeneratorTests.NoOpAotContributingBeanFactoryPostProcessor, \
org.springframework.context.generator.ApplicationContextAotGeneratorTests.TextAotContributingBeanFactoryPostProcessor

View File

@@ -1,2 +0,0 @@
org.springframework.aot.hint.RuntimeHintsRegistrar= \
org.springframework.context.generator.RuntimeHintsPostProcessorTests.SampleRuntimeHintsRegistrar