This commit is contained in:
Phillip Webb
2020-07-22 17:59:38 -07:00
parent da69286838
commit 14c1b0938a
10 changed files with 130 additions and 85 deletions

View File

@@ -16,8 +16,13 @@
package org.springframework.boot;
import java.util.function.Supplier;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* Strategy interface for creating the {@link ConfigurableApplicationContext} used by a
@@ -26,11 +31,33 @@ import org.springframework.context.ConfigurableApplicationContext;
* context.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 2.4.0
*/
@FunctionalInterface
public interface ApplicationContextFactory {
/**
* A default {@link ApplicationContextFactory} implementation that will create an
* appropriate context for the {@link WebApplicationType}.
*/
static ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
/**
* Creates the {@link ConfigurableApplicationContext application context} for a
* {@link SpringApplication}, respecting the given {@code webApplicationType}.
@@ -46,8 +73,19 @@ public interface ApplicationContextFactory {
* @return the factory that will instantiate the context class
* @see BeanUtils#instantiateClass(Class)
*/
static ApplicationContextFactory forContextClass(Class<? extends ConfigurableApplicationContext> contextClass) {
return (webApplicationType) -> BeanUtils.instantiateClass(contextClass);
static ApplicationContextFactory ofContextClass(Class<? extends ConfigurableApplicationContext> contextClass) {
return of(() -> BeanUtils.instantiateClass(contextClass));
}
/**
* Creates an {@code ApplicationContextFactory} that will create contexts by calling
* the given {@link Supplier}.
* @param supplier the context supplier, for example
* {@code AnnotationConfigApplicationContext::new}
* @return the factory that will instantiate the context class
*/
static ApplicationContextFactory of(Supplier<ConfigurableApplicationContext> supplier) {
return (webApplicationType) -> supplier.get();
}
}

View File

@@ -245,7 +245,7 @@ public class SpringApplication {
private boolean lazyInitialization = false;
private ApplicationContextFactory applicationContextFactory = new DefaultApplicationContextFactory();
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
/**
* Create a new {@link SpringApplication} instance. The application context will load
@@ -1150,7 +1150,7 @@ public class SpringApplication {
@Deprecated
public void setApplicationContextClass(Class<? extends ConfigurableApplicationContext> applicationContextClass) {
this.webApplicationType = WebApplicationType.deduceFromApplicationContext(applicationContextClass);
this.applicationContextFactory = ApplicationContextFactory.forContextClass(applicationContextClass);
this.applicationContextFactory = ApplicationContextFactory.ofContextClass(applicationContextClass);
}
/**
@@ -1164,7 +1164,8 @@ public class SpringApplication {
* @since 2.4.0
*/
public void setApplicationContextFactory(ApplicationContextFactory applicationContextFactory) {
this.applicationContextFactory = applicationContextFactory;
this.applicationContextFactory = (applicationContextFactory != null) ? applicationContextFactory
: ApplicationContextFactory.DEFAULT;
}
/**
@@ -1309,26 +1310,4 @@ public class SpringApplication {
return new LinkedHashSet<>(list);
}
private static final class DefaultApplicationContextFactory implements ApplicationContextFactory {
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextFactory", ex);
}
}
}
}

View File

@@ -42,8 +42,8 @@ class SpringApplicationNoWebTests {
@Test
void specificApplicationContextClass() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setApplicationContextFactory(
ApplicationContextFactory.forContextClass(StaticApplicationContext.class));
application
.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class));
ConfigurableApplicationContext context = application.run();
assertThat(context).isInstanceOf(StaticApplicationContext.class);
context.close();

View File

@@ -331,8 +331,8 @@ class SpringApplicationTests {
@Test
void specificApplicationContextFactory() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setApplicationContextFactory(
ApplicationContextFactory.forContextClass(StaticApplicationContext.class));
application
.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class));
this.context = application.run();
assertThat(this.context).isInstanceOf(StaticApplicationContext.class);
}
@@ -889,8 +889,7 @@ class SpringApplicationTests {
@Test
void registerShutdownHook() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application
.setApplicationContextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
this.context = application.run();
SpyApplicationContext applicationContext = (SpyApplicationContext) this.context;
verify(applicationContext.getApplicationContext()).registerShutdownHook();
@@ -899,8 +898,7 @@ class SpringApplicationTests {
@Test
void registerListener() {
SpringApplication application = new SpringApplication(ExampleConfig.class, ListenerConfig.class);
application
.setApplicationContextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
Set<ApplicationEvent> events = new LinkedHashSet<>();
application.addListeners((ApplicationListener<ApplicationEvent>) events::add);
this.context = application.run();
@@ -913,8 +911,7 @@ class SpringApplicationTests {
void registerListenerWithCustomMulticaster() {
SpringApplication application = new SpringApplication(ExampleConfig.class, ListenerConfig.class,
Multicaster.class);
application
.setApplicationContextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
Set<ApplicationEvent> events = new LinkedHashSet<>();
application.addListeners((ApplicationListener<ApplicationEvent>) events::add);
this.context = application.run();
@@ -994,8 +991,7 @@ class SpringApplicationTests {
@Test
void registerShutdownHookOff() {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application
.setApplicationContextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.setApplicationContextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
application.setRegisterShutdownHook(false);
this.context = application.run();
SpyApplicationContext applicationContext = (SpyApplicationContext) this.context;

View File

@@ -69,7 +69,7 @@ class SpringApplicationBuilderTests {
@Test
void profileAndProperties() {
SpringApplicationBuilder application = new SpringApplicationBuilder().sources(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(StaticApplicationContext.class))
.contextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class))
.profiles("foo").properties("foo=bar");
this.context = application.run();
assertThat(this.context).isInstanceOf(StaticApplicationContext.class);
@@ -80,7 +80,7 @@ class SpringApplicationBuilderTests {
@Test
void propertiesAsMap() {
SpringApplicationBuilder application = new SpringApplicationBuilder().sources(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(StaticApplicationContext.class))
.contextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class))
.properties(Collections.singletonMap("bar", "foo"));
this.context = application.run();
assertThat(this.context.getEnvironment().getProperty("bar")).isEqualTo("foo");
@@ -89,7 +89,7 @@ class SpringApplicationBuilderTests {
@Test
void propertiesAsProperties() {
SpringApplicationBuilder application = new SpringApplicationBuilder().sources(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(StaticApplicationContext.class))
.contextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class))
.properties(StringUtils.splitArrayElementsIntoProperties(new String[] { "bar=foo" }, "="));
this.context = application.run();
assertThat(this.context.getEnvironment().getProperty("bar")).isEqualTo("foo");
@@ -98,7 +98,7 @@ class SpringApplicationBuilderTests {
@Test
void propertiesWithRepeatSeparator() {
SpringApplicationBuilder application = new SpringApplicationBuilder().sources(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(StaticApplicationContext.class))
.contextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class))
.properties("one=c:\\logging.file.name", "two=a:b", "three:c:\\logging.file.name", "four:a:b");
this.context = application.run();
ConfigurableEnvironment environment = this.context.getEnvironment();
@@ -120,7 +120,7 @@ class SpringApplicationBuilderTests {
@Test
void specificApplicationContextFactory() {
SpringApplicationBuilder application = new SpringApplicationBuilder().sources(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(StaticApplicationContext.class));
.contextFactory(ApplicationContextFactory.ofContextClass(StaticApplicationContext.class));
this.context = application.run();
assertThat(this.context).isInstanceOf(StaticApplicationContext.class);
}
@@ -128,7 +128,7 @@ class SpringApplicationBuilderTests {
@Test
void parentContextCreationThatIsRunDirectly() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ChildConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
application.parent(ExampleConfig.class);
this.context = application.run("foo.bar=baz");
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent(any(ApplicationContext.class));
@@ -141,7 +141,7 @@ class SpringApplicationBuilderTests {
@Test
void parentContextCreationThatIsBuiltThenRun() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ChildConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
application.parent(ExampleConfig.class);
this.context = application.build("a=alpha").run("b=bravo");
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent(any(ApplicationContext.class));
@@ -153,7 +153,7 @@ class SpringApplicationBuilderTests {
@Test
void parentContextCreationWithChildShutdown() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ChildConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class))
.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class))
.registerShutdownHook(true);
application.parent(ExampleConfig.class);
this.context = application.run();
@@ -164,7 +164,7 @@ class SpringApplicationBuilderTests {
@Test
void contextWithClassLoader() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
application.resourceLoader(new DefaultResourceLoader(classLoader));
this.context = application.run();
@@ -174,7 +174,7 @@ class SpringApplicationBuilderTests {
@Test
void parentContextWithClassLoader() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ChildConfig.class)
.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
application.resourceLoader(new DefaultResourceLoader(classLoader));
application.parent(ExampleConfig.class);
@@ -186,7 +186,7 @@ class SpringApplicationBuilderTests {
void parentFirstCreation() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class)
.child(ChildConfig.class);
application.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
this.context = application.run();
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent(any(ApplicationContext.class));
assertThat(((SpyApplicationContext) this.context).getRegisteredShutdownHook()).isFalse();
@@ -243,7 +243,7 @@ class SpringApplicationBuilderTests {
void parentContextIdentical() {
SpringApplicationBuilder application = new SpringApplicationBuilder(ExampleConfig.class);
application.parent(ExampleConfig.class);
application.contextFactory(ApplicationContextFactory.forContextClass(SpyApplicationContext.class));
application.contextFactory(ApplicationContextFactory.ofContextClass(SpyApplicationContext.class));
this.context = application.run();
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent(any(ApplicationContext.class));
}