Browse Source

Apply active profiles consistently with AOT

Profiles that are active during AOT processing are automatically enabled
when the AOT initializer runs. While this works for an arrangement that
only relies on the ApplicationContext, it does not for Spring Boot that
has specific handling of profiles when it prepares the environment, way
before the ApplicationContext is event created.

This commit adds a specific contribution that generates a dedicated
EnvironmentPostProcessor. It also updates the handling of post
processors so that when AOT runs, the AOT generated one if it exists is
invoked first.

This has the effect of consistently activating such profiles in a
Spring Boot application.

Closes gh-41562
pull/42503/head
Stéphane Nicoll 1 year ago
parent
commit
6b216f1748
  1. 114
      spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java
  2. 1
      spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories
  3. 365
      spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java

114
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@ -16,9 +16,21 @@ @@ -16,9 +16,21 @@
package org.springframework.boot.env;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import javax.lang.model.element.Modifier;
import org.springframework.aot.AotDetector;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
@ -26,20 +38,29 @@ import org.springframework.boot.context.event.ApplicationFailedEvent; @@ -26,20 +38,29 @@ import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.javapoet.CodeBlock;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* {@link SmartApplicationListener} used to trigger {@link EnvironmentPostProcessor
* EnvironmentPostProcessors} registered in the {@code spring.factories} file.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 2.4.0
*/
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
private static final String AOT_FEATURE_NAME = "EnvironmentPostProcessor";
/**
* The default order for the processor.
*/
@ -104,8 +125,10 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica @@ -104,8 +125,10 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
List<EnvironmentPostProcessor> postProcessors = getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext());
addAotGeneratedEnvironmentPostProcessorIfNecessary(postProcessors, application);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(environment, application);
}
}
@ -129,6 +152,32 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica @@ -129,6 +152,32 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
private void addAotGeneratedEnvironmentPostProcessorIfNecessary(List<EnvironmentPostProcessor> postProcessors,
SpringApplication springApplication) {
if (AotDetector.useGeneratedArtifacts()) {
ClassLoader classLoader = (springApplication.getResourceLoader() != null)
? springApplication.getResourceLoader().getClassLoader() : null;
String postProcessorClassName = springApplication.getMainApplicationClass().getName() + "__"
+ AOT_FEATURE_NAME;
if (ClassUtils.isPresent(postProcessorClassName, classLoader)) {
postProcessors.add(0, instantiateEnvironmentPostProcessor(postProcessorClassName, classLoader));
}
}
}
private EnvironmentPostProcessor instantiateEnvironmentPostProcessor(String postProcessorClassName,
ClassLoader classLoader) {
try {
Class<?> initializerClass = ClassUtils.resolveClassName(postProcessorClassName, classLoader);
Assert.isAssignable(EnvironmentPostProcessor.class, initializerClass);
return (EnvironmentPostProcessor) BeanUtils.instantiateClass(initializerClass);
}
catch (BeanInstantiationException ex) {
throw new IllegalArgumentException(
"Failed to instantiate EnvironmentPostProcessor: " + postProcessorClassName, ex);
}
}
@Override
public int getOrder() {
return this.order;
@ -138,4 +187,63 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica @@ -138,4 +187,63 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica
this.order = order;
}
/**
* Contribute a {@code <Application>__EnvironmentPostProcessor} class that stores AOT
* optimizations.
*/
static class EnvironmentBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(
ConfigurableListableBeanFactory beanFactory) {
Environment environment = beanFactory.getBean(ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME,
Environment.class);
String[] activeProfiles = environment.getActiveProfiles();
String[] defaultProfiles = environment.getDefaultProfiles();
if (!ObjectUtils.isEmpty(activeProfiles) && !Arrays.equals(activeProfiles, defaultProfiles)) {
return new EnvironmentAotContribution(activeProfiles);
}
return null;
}
}
private static final class EnvironmentAotContribution implements BeanFactoryInitializationAotContribution {
private static final String ENVIRONMENT_VARIABLE = "environment";
private final String[] activeProfiles;
private EnvironmentAotContribution(String[] activeProfiles) {
this.activeProfiles = activeProfiles;
}
@Override
public void applyTo(GenerationContext generationContext,
BeanFactoryInitializationCode beanFactoryInitializationCode) {
GeneratedClass generatedClass = generationContext.getGeneratedClasses()
.addForFeature(AOT_FEATURE_NAME, (type) -> {
type.addModifiers(Modifier.PUBLIC);
type.addJavadoc("Configure the environment with AOT optimizations.");
type.addSuperinterface(EnvironmentPostProcessor.class);
});
generatedClass.getMethods().add("postProcessEnvironment", (method) -> {
method.addModifiers(Modifier.PUBLIC);
method.addAnnotation(Override.class);
method.addParameter(ConfigurableEnvironment.class, ENVIRONMENT_VARIABLE);
method.addParameter(SpringApplication.class, "application");
method.addCode(generateActiveProfilesInitializeCode());
});
}
private CodeBlock generateActiveProfilesInitializeCode() {
CodeBlock.Builder code = CodeBlock.builder();
for (String activeProfile : this.activeProfiles) {
code.addStatement("$L.addActiveProfile($S)", ENVIRONMENT_VARIABLE, activeProfile);
}
return code.build();
}
}
}

1
spring-boot-project/spring-boot/src/main/resources/META-INF/spring/aot.factories

@ -15,6 +15,7 @@ org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints @@ -15,6 +15,7 @@ org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints
org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.EnvironmentBeanFactoryInitializationAotProcessor,\
org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryInitializationAotProcessor
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\

365
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListenerTests.java vendored

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@ -16,25 +16,56 @@ @@ -16,25 +16,56 @@
package org.springframework.boot.env;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.aot.AotDetector;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.DefaultBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.EnvironmentBeanFactoryInitializationAotProcessor;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.boot.logging.DeferredLogs;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.aot.ApplicationContextAotGenerator;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.ClassName;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
@ -45,98 +76,286 @@ import static org.mockito.Mockito.spy; @@ -45,98 +76,286 @@ import static org.mockito.Mockito.spy;
* Tests for {@link EnvironmentPostProcessorApplicationListener}.
*
* @author Phillip Webb
* @author Stephane Nicoll
*/
class EnvironmentPostProcessorApplicationListenerTests {
private final DeferredLogs deferredLogs = spy(new DeferredLogs());
@Nested
class ListenerTests {
private final DefaultBootstrapContext bootstrapContext = spy(new DefaultBootstrapContext());
private final DeferredLogs deferredLogs = spy(new DeferredLogs());
private final EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
private final DefaultBootstrapContext bootstrapContext = spy(new DefaultBootstrapContext());
@BeforeEach
void setup() {
ReflectionTestUtils.setField(this.listener, "deferredLogs", this.deferredLogs);
ReflectionTestUtils.setField(this.listener, "postProcessorsFactory",
(Function<ClassLoader, EnvironmentPostProcessorsFactory>) (
classLoader) -> EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
}
private final EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
@Test
void createUsesSpringFactories() {
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
assertThat(listener.getEnvironmentPostProcessors(null, this.bootstrapContext)).hasSizeGreaterThan(1);
}
@BeforeEach
void setup() {
ReflectionTestUtils.setField(this.listener, "deferredLogs", this.deferredLogs);
ReflectionTestUtils.setField(this.listener, "postProcessorsFactory",
(Function<ClassLoader, EnvironmentPostProcessorsFactory>) (
classLoader) -> EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
}
@Test
void createWhenHasFactoryUsesFactory() {
EnvironmentPostProcessorApplicationListener listener = EnvironmentPostProcessorApplicationListener
.with(EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
List<EnvironmentPostProcessor> postProcessors = listener.getEnvironmentPostProcessors(null,
this.bootstrapContext);
assertThat(postProcessors).hasSize(1);
assertThat(postProcessors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
}
@Test
void createUsesSpringFactories() {
EnvironmentPostProcessorApplicationListener listener = new EnvironmentPostProcessorApplicationListener();
assertThat(listener.getEnvironmentPostProcessors(null, this.bootstrapContext)).hasSizeGreaterThan(1);
}
@Test
void supportsEventTypeWhenApplicationEnvironmentPreparedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationEnvironmentPreparedEvent.class)).isTrue();
}
@Test
void createWhenHasFactoryUsesFactory() {
EnvironmentPostProcessorApplicationListener listener = EnvironmentPostProcessorApplicationListener
.with(EnvironmentPostProcessorsFactory.of(TestEnvironmentPostProcessor.class));
List<EnvironmentPostProcessor> postProcessors = listener.getEnvironmentPostProcessors(null,
this.bootstrapContext);
assertThat(postProcessors).hasSize(1);
assertThat(postProcessors.get(0)).isInstanceOf(TestEnvironmentPostProcessor.class);
}
@Test
void supportsEventTypeWhenApplicationPreparedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationPreparedEvent.class)).isTrue();
}
@Test
void supportsEventTypeWhenApplicationEnvironmentPreparedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationEnvironmentPreparedEvent.class)).isTrue();
}
@Test
void supportsEventTypeWhenApplicationFailedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationFailedEvent.class)).isTrue();
}
@Test
void supportsEventTypeWhenApplicationPreparedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationPreparedEvent.class)).isTrue();
}
@Test
void supportsEventTypeWhenOtherEventReturnsFalse() {
assertThat(this.listener.supportsEventType(ApplicationStartingEvent.class)).isFalse();
}
@Test
void supportsEventTypeWhenApplicationFailedEventReturnsTrue() {
assertThat(this.listener.supportsEventType(ApplicationFailedEvent.class)).isTrue();
}
@Test
void onApplicationEventWhenApplicationEnvironmentPreparedEventCallsPostProcessors() {
SpringApplication application = mock(SpringApplication.class);
MockEnvironment environment = new MockEnvironment();
ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(this.bootstrapContext,
application, new String[0], environment);
this.listener.onApplicationEvent(event);
assertThat(environment.getProperty("processed")).isEqualTo("true");
}
@Test
void supportsEventTypeWhenOtherEventReturnsFalse() {
assertThat(this.listener.supportsEventType(ApplicationStartingEvent.class)).isFalse();
}
@Test
void onApplicationEventWhenApplicationPreparedEventSwitchesLogs() {
SpringApplication application = mock(SpringApplication.class);
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
ApplicationPreparedEvent event = new ApplicationPreparedEvent(application, new String[0], context);
this.listener.onApplicationEvent(event);
then(this.deferredLogs).should().switchOverAll();
}
@Test
void onApplicationEventWhenApplicationEnvironmentPreparedEventCallsPostProcessors() {
SpringApplication application = mock(SpringApplication.class);
MockEnvironment environment = new MockEnvironment();
ApplicationEnvironmentPreparedEvent event = new ApplicationEnvironmentPreparedEvent(this.bootstrapContext,
application, new String[0], environment);
this.listener.onApplicationEvent(event);
assertThat(environment.getProperty("processed")).isEqualTo("true");
}
@Test
void onApplicationEventWhenApplicationPreparedEventSwitchesLogs() {
SpringApplication application = mock(SpringApplication.class);
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
ApplicationPreparedEvent event = new ApplicationPreparedEvent(application, new String[0], context);
this.listener.onApplicationEvent(event);
then(this.deferredLogs).should().switchOverAll();
}
@Test
void onApplicationEventWhenApplicationFailedEventSwitchesLogs() {
SpringApplication application = mock(SpringApplication.class);
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
ApplicationFailedEvent event = new ApplicationFailedEvent(application, new String[0], context,
new RuntimeException());
this.listener.onApplicationEvent(event);
then(this.deferredLogs).should().switchOverAll();
}
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor {
TestEnvironmentPostProcessor(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry) {
assertThat(logFactory).isNotNull();
assertThat(bootstrapRegistry).isNotNull();
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
((MockEnvironment) environment).setProperty("processed", "true");
}
}
@Test
void onApplicationEventWhenApplicationFailedEventSwitchesLogs() {
SpringApplication application = mock(SpringApplication.class);
ConfigurableApplicationContext context = mock(ConfigurableApplicationContext.class);
ApplicationFailedEvent event = new ApplicationFailedEvent(application, new String[0], context,
new RuntimeException());
this.listener.onApplicationEvent(event);
then(this.deferredLogs).should().switchOverAll();
}
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Nested
class AotTests {
TestEnvironmentPostProcessor(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry) {
assertThat(logFactory).isNotNull();
assertThat(bootstrapRegistry).isNotNull();
private static final ClassName TEST_APP = ClassName.get("com.example", "TestApp");
@Test
void aotContributionIsNotNecessaryWithDefaultConfiguration() {
assertThat(getContribution(new StandardEnvironment())).isNull();
}
@Test
void aotContributionIsNotNecessaryWithDefaultProfileActive() {
StandardEnvironment environment = new StandardEnvironment();
environment.setDefaultProfiles("fallback");
environment.setActiveProfiles("fallback");
assertThat(getContribution(environment)).isNull();
}
@Test
void aotContributionRegistersActiveProfiles() {
ConfigurableEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles("one", "two");
compile(createContext(environment), (compiled) -> {
EnvironmentPostProcessor environmentPostProcessor = compiled.getInstance(EnvironmentPostProcessor.class,
ClassName.get("com.example", "TestApp__EnvironmentPostProcessor").toString());
StandardEnvironment freshEnvironment = new StandardEnvironment();
environmentPostProcessor.postProcessEnvironment(freshEnvironment, new SpringApplication());
assertThat(freshEnvironment.getActiveProfiles()).containsExactly("one", "two");
});
}
@Test
void shouldUseAotEnvironmentPostProcessor() {
SpringApplication application = new SpringApplication(ExampleAotProcessedApp.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.setMainApplicationClass(ExampleAotProcessedApp.class);
System.setProperty(AotDetector.AOT_ENABLED, "true");
try {
ApplicationContext context = application.run();
assertThat(context.getEnvironment().getActiveProfiles()).containsExactly("one", "three");
assertThat(context.getBean("test")).isEqualTo("test");
}
finally {
System.clearProperty(AotDetector.AOT_ENABLED);
}
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
((MockEnvironment) environment).setProperty("processed", "true");
@Test
void aotEnvironmentPostProcessorShouldBeAppliedFirst(@TempDir Path tempDir) {
Properties properties = new Properties();
properties.put(EnvironmentPostProcessor.class.getName(), TestEnvironmentPostProcessor.class.getName());
ClassLoader classLoader = createClassLoaderWithAdditionalSpringFactories(tempDir, properties);
DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader);
SpringApplication application = new SpringApplication(ExampleAotProcessedApp.class);
application.setResourceLoader(resourceLoader);
application.setWebApplicationType(WebApplicationType.NONE);
application.setMainApplicationClass(ExampleAotProcessedApp.class);
System.setProperty(AotDetector.AOT_ENABLED, "true");
try {
ApplicationContext context = application.run();
// See TestEnvironmentPostProcessor
assertThat(context.getEnvironment().getProperty("test.activeProfiles")).isEqualTo("one,three");
assertThat(context.getEnvironment().getActiveProfiles()).containsExactly("one", "three");
assertThat(context.getBean("test")).isEqualTo("test");
}
finally {
System.clearProperty(AotDetector.AOT_ENABLED);
}
}
@Test
void shouldBeLenientIfAotEnvironmentPostProcessorDoesNotExist() {
SpringApplication application = new SpringApplication(ExampleAotProcessedNoProfileApp.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.setMainApplicationClass(ExampleAotProcessedNoProfileApp.class);
System.setProperty(AotDetector.AOT_ENABLED, "true");
try {
ApplicationContext context = application.run();
assertThat(context.getEnvironment().getActiveProfiles()).isEmpty();
assertThat(context.getBean("test")).isEqualTo("test");
}
finally {
System.clearProperty(AotDetector.AOT_ENABLED);
}
}
private BeanFactoryInitializationAotContribution getContribution(ConfigurableEnvironment environment) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton(ConfigurableApplicationContext.ENVIRONMENT_BEAN_NAME, environment);
return new EnvironmentBeanFactoryInitializationAotProcessor().processAheadOfTime(beanFactory);
}
private GenericApplicationContext createContext(ConfigurableEnvironment environment) {
GenericApplicationContext context = new GenericApplicationContext();
context.setEnvironment(environment);
return context;
}
private void compile(GenericApplicationContext context, Consumer<Compiled> compiled) {
TestGenerationContext generationContext = new TestGenerationContext(TEST_APP);
new ApplicationContextAotGenerator().processAheadOfTime(context, generationContext);
generationContext.writeGeneratedContent();
TestCompiler.forSystem().with(generationContext).compile(compiled);
}
private ClassLoader createClassLoaderWithAdditionalSpringFactories(Path tempDir, Properties properties) {
return new ClassLoader() {
@Override
public Enumeration<URL> getResources(String name) throws IOException {
Enumeration<URL> resources = super.getResources(name);
if (SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION.equals(name)) {
Path springFactories = tempDir.resolve("spring.factories");
try (BufferedWriter writer = Files.newBufferedWriter(springFactories)) {
properties.store(writer, "");
}
List<URL> allResources = new ArrayList<>();
allResources.add(springFactories.toUri().toURL());
allResources.addAll(Collections.list(resources));
return Collections.enumeration(allResources);
}
return resources;
}
};
}
static class TestEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MockPropertySource propertySource = new MockPropertySource().withProperty("test.activeProfiles",
StringUtils.arrayToCommaDelimitedString(environment.getActiveProfiles()));
environment.getPropertySources().addLast(propertySource);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
static class ExampleAotProcessedApp {
}
static class ExampleAotProcessedApp__ApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton("test", "test");
}
}
static class ExampleAotProcessedApp__EnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
environment.addActiveProfile("one");
environment.addActiveProfile("three");
}
}
static class ExampleAotProcessedNoProfileApp {
}
static class ExampleAotProcessedNoProfileApp__ApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getBeanFactory().registerSingleton("test", "test");
}
}
}

Loading…
Cancel
Save