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

@@ -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);
}
}
}