Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit 02a7abc

Browse files
committed
Configure Environment for AOT generation
Prior to this commit, the `ContextBootstrapContributor` would prepare an empty `Environment` and only apply some defaults to it. This lead to several sample applications failing as properties provided by the application (in `application.properties`) would not be considered. As a consequence, all `@ConditionalOnProperty` on `*.enabled` configuration keys would be discarded at build time if not enabled by default in Spring Boot. This commit loads a minimal infrastructure and prepares an `Environment` that partially loads the application configuration at build time. There are many limitations with this current approach: * this doesn't support many customizations on the `SpringApplication` * this doesn't load `Bootstrapper` so will fail with configuration trying to resolve properties using `Bootstrappers`, like Eureka. Fixes gh-992
1 parent 721eb35 commit 02a7abc

File tree

1 file changed

+86
-37
lines changed

1 file changed

+86
-37
lines changed

spring-aot/src/main/java/org/springframework/aot/context/bootstrap/ContextBootstrapContributor.java

+86-37
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,27 @@
2525
import org.springframework.aot.BuildContext;
2626
import org.springframework.aot.SourceFiles;
2727
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
28+
import org.springframework.boot.DefaultBootstrapContext;
29+
import org.springframework.boot.SpringApplication;
30+
import org.springframework.boot.WebApplicationType;
31+
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
32+
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
33+
import org.springframework.boot.convert.ApplicationConversionService;
34+
import org.springframework.boot.logging.DeferredLogs;
2835
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
36+
import org.springframework.boot.web.reactive.context.StandardReactiveWebEnvironment;
2937
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
3038
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3139
import org.springframework.context.annotation.BuildTimeBeanDefinitionsRegistrar;
3240
import org.springframework.context.bootstrap.generator.BootstrapGenerationResult;
3341
import org.springframework.context.bootstrap.generator.ContextBootstrapGenerator;
3442
import org.springframework.context.support.GenericApplicationContext;
35-
import org.springframework.core.env.ConfigurableEnvironment;
3643
import org.springframework.core.env.PropertiesPropertySource;
3744
import org.springframework.core.env.StandardEnvironment;
38-
import org.springframework.core.type.classreading.ClassDescriptor;
39-
import org.springframework.core.type.classreading.TypeSystem;
45+
import org.springframework.core.io.ResourceLoader;
4046
import org.springframework.nativex.AotOptions;
4147
import org.springframework.util.ClassUtils;
48+
import org.springframework.web.context.support.StandardServletEnvironment;
4249

4350
/**
4451
* @author Brian Clozel
@@ -48,64 +55,98 @@
4855
public class ContextBootstrapContributor implements BootstrapContributor {
4956

5057
// Copied from Spring Boot WebApplicationType
51-
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
52-
"org.springframework.web.context.ConfigurableWebApplicationContext" };
58+
private static final String[] SERVLET_INDICATOR_CLASSES = {"javax.servlet.Servlet",
59+
"org.springframework.web.context.ConfigurableWebApplicationContext"};
60+
5361
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
62+
5463
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
64+
5565
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
5666

5767
private static final Log logger = LogFactory.getLog(ContextBootstrapContributor.class);
5868

5969
@Override
6070
public void contribute(BuildContext context, AotOptions aotOptions) {
61-
TypeSystem typeSystem = context.getTypeSystem();
62-
ClassLoader classLoader = typeSystem.getResourceLoader().getClassLoader();
71+
ResourceLoader resourceLoader = context.getTypeSystem().getResourceLoader();
72+
ClassLoader classLoader = resourceLoader.getClassLoader();
73+
Class<?> mainClass;
74+
String mainClassName = context.getMainClass();
75+
try {
76+
logger.info("Detected main class: " + mainClassName);
77+
mainClass = ClassUtils.forName(mainClassName, classLoader);
78+
}
79+
catch (ClassNotFoundException exc) {
80+
throw new IllegalStateException("Could not load main class" + mainClassName, exc);
81+
}
6382

64-
// TODO: detect correct type of application context
65-
GenericApplicationContext applicationContext = createApplicationContext(typeSystem);
66-
applicationContext.setResourceLoader(typeSystem.getResourceLoader());
83+
WebApplicationType webApplicationType = deduceWebApplicationType(classLoader);
84+
GenericApplicationContext applicationContext = createApplicationContext(webApplicationType);
85+
applicationContext.setResourceLoader(resourceLoader);
86+
87+
StandardEnvironment environment = createEnvironment(webApplicationType);
88+
configureEnvironment(environment, resourceLoader, mainClass);
6789

68-
// TODO: pre-compute environment from properties?
69-
ConfigurableEnvironment environment = new StandardEnvironment();
70-
Properties properties = new Properties();
71-
properties.put("spring.aop.proxy-target-class", "false"); // Not supported in native images
72-
properties.put("spring.cloud.refresh.enabled", "false"); // Sampler is a class and can't be proxied
73-
properties.put("spring.sleuth.async.enabled", "false"); // Too much proxy created
74-
properties.put("spring.devtools.restart.enabled", "false"); // Deactivate dev tools
75-
environment.getPropertySources().addFirst(new PropertiesPropertySource("native", properties));
7690
applicationContext.setEnvironment(environment);
91+
applicationContext.registerBean(mainClass);
7792

78-
// TODO: auto-detect main class
79-
String mainClassName = context.getMainClass();
80-
if (mainClassName != null) {
81-
ClassDescriptor mainClass = typeSystem.resolveClass(mainClassName);
82-
logger.info("Detected main class: " + mainClass.getCanonicalClassName());
83-
try {
84-
applicationContext.registerBean(ClassUtils.forName(mainClass.getCanonicalClassName(), classLoader));
85-
}
86-
catch (ClassNotFoundException exc) {
87-
throw new IllegalStateException("Could not load main class" + mainClass.getCanonicalClassName(), exc);
88-
}
89-
}
9093
ConfigurableListableBeanFactory beanFactory = new BuildTimeBeanDefinitionsRegistrar().processBeanDefinitions(applicationContext);
9194
ContextBootstrapGenerator bootstrapGenerator = new ContextBootstrapGenerator(classLoader);
9295
BootstrapGenerationResult bootstrapGenerationResult = bootstrapGenerator.generateBootstrapClass(beanFactory, "org.springframework.aot");
9396
bootstrapGenerationResult.getSourceFiles().forEach(javaFile -> context.addSourceFiles(SourceFiles.fromJavaFile(javaFile)));
9497
context.describeReflection((reflectionDescriptor) -> bootstrapGenerationResult.getClassDescriptors().forEach(reflectionDescriptor::merge));
9598
}
9699

100+
private void configureEnvironment(StandardEnvironment environment, ResourceLoader resourceLoader, Class<?> mainClass) {
101+
environment.setConversionService(new ApplicationConversionService());
102+
ConfigurationPropertySources.attach(environment);
103+
ConfigDataEnvironmentPostProcessor configDataEnvironmentPostProcessor =
104+
new ConfigDataEnvironmentPostProcessor(new DeferredLogs(), new DefaultBootstrapContext());
105+
106+
SpringApplication application = new SpringApplication(resourceLoader, mainClass);
107+
configDataEnvironmentPostProcessor.postProcessEnvironment(environment, application);
108+
109+
Properties properties = new Properties();
110+
properties.put("spring.aop.proxy-target-class", "false"); // Not supported in native images
111+
properties.put("spring.cloud.refresh.enabled", "false"); // Sampler is a class and can't be proxied
112+
properties.put("spring.sleuth.async.enabled", "false"); // Too much proxy created
113+
properties.put("spring.devtools.restart.enabled", "false"); // Deactivate dev tools
114+
environment.getPropertySources().addFirst(new PropertiesPropertySource("native", properties));
115+
}
116+
97117
// TODO Avoid duplication with WebApplicationType and SpringAotApplication.AOT_FACTORY
98-
private GenericApplicationContext createApplicationContext(TypeSystem typeSystem) {
99-
if (typeSystem.resolveClass(WEBFLUX_INDICATOR_CLASS) != null && typeSystem.resolveClass(WEBMVC_INDICATOR_CLASS) == null
100-
&& typeSystem.resolveClass(JERSEY_INDICATOR_CLASS) == null) {
101-
return ReactiveContextDelegate.createApplicationContext();
118+
private GenericApplicationContext createApplicationContext(WebApplicationType webApplicationType) {
119+
switch (webApplicationType) {
120+
case REACTIVE:
121+
return ReactiveContextDelegate.createApplicationContext();
122+
case SERVLET:
123+
return ServletContextDelegate.createApplicationContext();
124+
}
125+
return new AnnotationConfigApplicationContext();
126+
}
127+
128+
private StandardEnvironment createEnvironment(WebApplicationType webApplicationType) {
129+
switch (webApplicationType) {
130+
case SERVLET:
131+
return ServletEnvironmentDelegate.createServletEnvironment();
132+
case REACTIVE:
133+
return new StandardReactiveWebEnvironment();
134+
default:
135+
return new StandardEnvironment();
136+
}
137+
}
138+
139+
private WebApplicationType deduceWebApplicationType(ClassLoader classLoader) {
140+
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, classLoader) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, classLoader)
141+
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, classLoader)) {
142+
return WebApplicationType.REACTIVE;
102143
}
103144
for (String className : SERVLET_INDICATOR_CLASSES) {
104-
if (typeSystem.resolveClass(className) == null) {
105-
return new AnnotationConfigApplicationContext();
145+
if (!ClassUtils.isPresent(className, classLoader)) {
146+
return WebApplicationType.NONE;
106147
}
107148
}
108-
return ServletContextDelegate.createApplicationContext();
149+
return WebApplicationType.SERVLET;
109150
}
110151

111152
// To avoid NoClassDefFoundError:
@@ -116,6 +157,14 @@ public static GenericApplicationContext createApplicationContext() {
116157
}
117158
}
118159

160+
// To avoid NoClassDefFoundError:
161+
static class ServletEnvironmentDelegate {
162+
163+
public static StandardEnvironment createServletEnvironment() {
164+
return new StandardServletEnvironment();
165+
}
166+
}
167+
119168
// To avoid NoClassDefFoundError:
120169
static class ReactiveContextDelegate {
121170

0 commit comments

Comments
 (0)