Browse Source
Builder for SpringApplication and ApplicationContext instances with
convenient fluent API and context hierarchy support. Simple example
of a context hierarchy:
new SpringApplicationBuilder(ParentConfig.class)
.child(ChildConfig.class).run(args);
Another common use case is setting default arguments, e.g.
active Spring profiles, to set up the environment for an application:
new SpringApplicationBuilder(Application.class).profiles("server")
.defaultArgs("--transport=local").run(args);
If your needs are simpler, consider using the static convenience
methods in SpringApplication instead.
[#49703716] [bs-116] Parent context for some beans maybe?
pull/67/head
14 changed files with 731 additions and 121 deletions
@ -0,0 +1,405 @@
@@ -0,0 +1,405 @@
|
||||
/* |
||||
* Copyright 2012-2013 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 |
||||
* |
||||
* http://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.boot.builder; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.context.initializer.ParentContextApplicationContextInitializer; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.core.env.ConfigurableEnvironment; |
||||
import org.springframework.core.io.ResourceLoader; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* Builder for {@link SpringApplication} and {@link ApplicationContext} instances with |
||||
* convenient fluent API and context hierarchy support. Simple example of a context |
||||
* hierarchy: |
||||
* |
||||
* <pre> |
||||
* new SpringApplicationBuilder(ParentConfig.class).child(ChildConfig.class).run(args); |
||||
* </pre> |
||||
* |
||||
* Another common use case is setting default arguments, e.g. active Spring profiles, to |
||||
* set up the environment for an application: |
||||
* |
||||
* <pre> |
||||
* new SpringApplicationBuilder(Application.class).profiles("server") |
||||
* .defaultArgs("--transport=local").run(args); |
||||
* </pre> |
||||
* |
||||
* <p> |
||||
* If your needs are simpler, consider using the static convenience methods in |
||||
* SpringApplication instead. |
||||
* |
||||
* @author Dave Syer |
||||
* |
||||
*/ |
||||
public class SpringApplicationBuilder { |
||||
|
||||
private SpringApplication application; |
||||
private ConfigurableApplicationContext context; |
||||
private SpringApplicationBuilder parent; |
||||
private AtomicBoolean running = new AtomicBoolean(false); |
||||
private Set<Object> sources = new LinkedHashSet<Object>(); |
||||
private String[] defaultArgs; |
||||
private ConfigurableEnvironment environment; |
||||
|
||||
public SpringApplicationBuilder(Object... sources) { |
||||
this.application = new SpringApplication(sources); |
||||
} |
||||
|
||||
/** |
||||
* Accessor for the current application context. |
||||
* |
||||
* @return the current application context (or null if not yet running) |
||||
*/ |
||||
public ConfigurableApplicationContext context() { |
||||
return this.context; |
||||
} |
||||
|
||||
/** |
||||
* Accessor for the current application. |
||||
* |
||||
* @return the current application (never null) |
||||
*/ |
||||
public SpringApplication application() { |
||||
return this.application; |
||||
} |
||||
|
||||
/** |
||||
* Create an application context (and its parent if specified) with the command line |
||||
* args provided. The parent is run first with the same arguments if has not yet been |
||||
* started. |
||||
* |
||||
* @param args the command line arguments |
||||
* @return an application context created from the current state |
||||
*/ |
||||
public ConfigurableApplicationContext run(String... args) { |
||||
|
||||
if (this.parent != null) { |
||||
// If there is a parent initialize it and make sure it is added to the current
|
||||
// context
|
||||
addInitializers(new ParentContextApplicationContextInitializer( |
||||
this.parent.run(args))); |
||||
} |
||||
|
||||
if (this.running.get()) { |
||||
// If already created we just return the existing context
|
||||
return this.context; |
||||
} |
||||
|
||||
if (this.running.compareAndSet(false, true)) { |
||||
synchronized (this.running) { |
||||
// If not already running copy the sources over and then run.
|
||||
this.application.setSources(this.sources); |
||||
this.context = this.application.run(args); |
||||
} |
||||
} |
||||
|
||||
return this.context; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Create a child application with the provided sources. Default args and environment |
||||
* are copied down into the child, but everything else is a clean sheet. |
||||
* |
||||
* @param sources the sources for the application (Spring configuration) |
||||
* @return the child application nuilder |
||||
*/ |
||||
public SpringApplicationBuilder child(Object... sources) { |
||||
|
||||
SpringApplicationBuilder child = new SpringApplicationBuilder(); |
||||
child.sources(sources); |
||||
|
||||
// Copy environment stuff from parent to child
|
||||
child.defaultArgs(this.defaultArgs).environment(this.environment); |
||||
child.parent = this; |
||||
|
||||
// It's not possible if embedded containers are enabled to support web contexts as
|
||||
// parents because the servlets cannot be initialized at the right point in
|
||||
// lifecycle.
|
||||
web(false); |
||||
|
||||
// Probably not interested in multiple banners
|
||||
showBanner(false); |
||||
|
||||
// Make sure sources get copied over
|
||||
this.application.setSources(this.sources); |
||||
|
||||
return child; |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Add a parent application with the provided sources. Default args and environment |
||||
* are copied up into the parent, but everything else is a clean sheet. |
||||
* |
||||
* @param sources the sources for the application (Spring configuration) |
||||
* @return the parent builder |
||||
*/ |
||||
public SpringApplicationBuilder parent(Object... sources) { |
||||
if (this.parent == null) { |
||||
this.parent = new SpringApplicationBuilder(sources).web(false) |
||||
.defaultArgs(this.defaultArgs).environment(this.environment); |
||||
} |
||||
else { |
||||
this.parent.sources(sources); |
||||
} |
||||
return this.parent; |
||||
} |
||||
|
||||
private SpringApplicationBuilder runAndExtractParent(String... args) { |
||||
if (this.context == null) { |
||||
run(args); |
||||
} |
||||
if (this.parent != null) { |
||||
return this.parent; |
||||
} |
||||
throw new IllegalStateException( |
||||
"No parent defined yet (please use the other overloaded parent methods to set one)"); |
||||
} |
||||
|
||||
/** |
||||
* Add an already running parent context to an existing application. |
||||
* |
||||
* @param parent the parent context |
||||
* @return the current builder (not the parent) |
||||
*/ |
||||
public SpringApplicationBuilder parent(ConfigurableApplicationContext parent) { |
||||
this.parent = new SpringApplicationBuilder(); |
||||
this.parent.context = parent; |
||||
this.parent.running.set(true); |
||||
addInitializers(new ParentContextApplicationContextInitializer(parent)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Create a sibling application (one with the same parent). A side effect of calling |
||||
* this method is that the current application (and its parent) are started. |
||||
* |
||||
* @param sources the sources for the application (Spring configuration) |
||||
* |
||||
* @return the new sibling builder |
||||
*/ |
||||
public SpringApplicationBuilder sibling(Object... sources) { |
||||
return runAndExtractParent().child(sources); |
||||
} |
||||
|
||||
/** |
||||
* Create a sibling application (one with the same parent). A side effect of calling |
||||
* this method is that the current application (and its parent) are started if they |
||||
* are not already running. |
||||
* |
||||
* @param sources the sources for the application (Spring configuration) |
||||
* @param args the command line arguments to use when starting the current app and its |
||||
* parent |
||||
* |
||||
* @return the new sibling builder |
||||
*/ |
||||
public SpringApplicationBuilder sibling(Object[] sources, String... args) { |
||||
return runAndExtractParent(args).child(sources); |
||||
} |
||||
|
||||
/** |
||||
* Explicitly set the context class to be used. |
||||
* |
||||
* @param cls the context class to use |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder contextClass( |
||||
Class<? extends ConfigurableApplicationContext> cls) { |
||||
this.application.setApplicationContextClass(cls); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add more sources to use in this application. |
||||
* |
||||
* @param sources the sources to add |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder sources(Object... sources) { |
||||
this.sources.addAll(new LinkedHashSet<Object>(Arrays.asList(sources))); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add more sources (configuration classes and components) to this application |
||||
* |
||||
* @param sources the sources to add |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder sources(Class<?>... sources) { |
||||
this.sources.addAll(new LinkedHashSet<Object>(Arrays.asList(sources))); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Flag to explicitly request a web or non-web environment (auto detected based on |
||||
* classpath if not set). |
||||
* |
||||
* @param webEnvironment the flag to set |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder web(boolean webEnvironment) { |
||||
this.application.setWebEnvironment(webEnvironment); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Flag to indicate the startup information should be logged. |
||||
* |
||||
* @param logStartupInfo the flag to set. Default true. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder logStartupInfo(boolean logStartupInfo) { |
||||
this.application.setLogStartupInfo(logStartupInfo); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Flag to indicate the startup banner should be printed. |
||||
* |
||||
* @param showBanner the flag to set. Default true. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder showBanner(boolean showBanner) { |
||||
this.application.setShowBanner(showBanner); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Fixes the main application class that is used to anchor the startup messages. |
||||
* |
||||
* @param mainApplicationClass the class to use. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder main(Class<?> mainApplicationClass) { |
||||
this.application.setMainApplicationClass(mainApplicationClass); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Flag to indicate that command line arguments should be added to the environment. |
||||
* |
||||
* @param addCommandLineProperties the flag to set. Default true. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder addCommandLineProperties( |
||||
boolean addCommandLineProperties) { |
||||
this.application.setAddCommandLineProperties(addCommandLineProperties); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Default command line arguments (overridden by explicit arguments at runtime in |
||||
* {@link #run(String...)}). |
||||
* |
||||
* @param defaultArgs the args to set. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder defaultArgs(String... defaultArgs) { |
||||
this.defaultArgs = defaultArgs; |
||||
this.application.setDefaultArgs(defaultArgs); |
||||
if (this.parent != null) { |
||||
this.parent.defaultArgs(defaultArgs); |
||||
this.parent.environment(this.environment); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set the active Spring profiles for this app (and its parent and children). |
||||
* Synonymous with {@link #defaultArgs(String...) |
||||
* defaultArgs("--spring.profiles.active=[profiles]")} |
||||
* |
||||
* @param profiles the profiles to set. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder profiles(String... profiles) { |
||||
defaultArgs("--spring.profiles.active=" |
||||
+ StringUtils.arrayToCommaDelimitedString(profiles)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Bean name generator for automatically generated bean names in the application |
||||
* context. |
||||
* |
||||
* @param beanNameGenerator the generator to set. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder beanNameGenerator(BeanNameGenerator beanNameGenerator) { |
||||
this.application.setBeanNameGenerator(beanNameGenerator); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Environment for the application context. |
||||
* |
||||
* @param environment the environment to set. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder environment(ConfigurableEnvironment environment) { |
||||
this.application.setEnvironment(environment); |
||||
this.environment = environment; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* {@link ResourceLoader} for the application context. If a custom class loader is |
||||
* needed, this is where it would be added. |
||||
* |
||||
* @param resourceLoader the resource loader to set. |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder resourceLoader(ResourceLoader resourceLoader) { |
||||
this.application.setResourceLoader(resourceLoader); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add some initializers to the application (applied to the {@link ApplicationContext} |
||||
* before any bean definitions are loaded). |
||||
* |
||||
* @param initializers some initializers to add |
||||
* @return the current builder |
||||
*/ |
||||
public SpringApplicationBuilder initializers( |
||||
ApplicationContextInitializer<?>... initializers) { |
||||
addInitializers(initializers); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* @param initializers the initializers to add |
||||
*/ |
||||
private void addInitializers(ApplicationContextInitializer<?>... initializers) { |
||||
Set<ApplicationContextInitializer<?>> target = new LinkedHashSet<ApplicationContextInitializer<?>>( |
||||
this.application.getInitializers()); |
||||
target.addAll(Arrays.asList(initializers)); |
||||
this.application.setInitializers(target); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
/* |
||||
* Copyright 2010-2012 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 |
||||
* |
||||
* http://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.boot.context.initializer; |
||||
|
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
/** |
||||
* {@link ApplicationContextInitializer} for setting the parent context. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class ParentContextApplicationContextInitializer implements |
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { |
||||
|
||||
private int order = Integer.MIN_VALUE; |
||||
|
||||
private ApplicationContext parent; |
||||
|
||||
public ParentContextApplicationContextInitializer(ApplicationContext parent) { |
||||
this.parent = parent; |
||||
} |
||||
|
||||
public void setOrder(int order) { |
||||
this.order = order; |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return this.order; |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(ConfigurableApplicationContext applicationContext) { |
||||
applicationContext.setParent(this.parent); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -0,0 +1,58 @@
|
||||
/* |
||||
* Copyright 2010-2012 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 |
||||
* |
||||
* http://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.boot.context.initializer; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
|
||||
import org.springframework.context.ApplicationContextInitializer; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.web.context.ConfigurableWebApplicationContext; |
||||
|
||||
/** |
||||
* {@link ApplicationContextInitializer} for setting the servlet context. |
||||
* |
||||
* @author Dave Syer |
||||
*/ |
||||
public class ServletContextApplicationContextInitializer implements |
||||
ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered { |
||||
|
||||
private int order = Integer.MIN_VALUE; |
||||
|
||||
private ServletContext servletContext; |
||||
|
||||
/** |
||||
* @param servletContext |
||||
*/ |
||||
public ServletContextApplicationContextInitializer(ServletContext servletContext) { |
||||
this.servletContext = servletContext; |
||||
} |
||||
|
||||
public void setOrder(int order) { |
||||
this.order = order; |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return this.order; |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(ConfigurableWebApplicationContext applicationContext) { |
||||
applicationContext.setServletContext(this.servletContext); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,112 @@
@@ -0,0 +1,112 @@
|
||||
/* |
||||
* Copyright 2012-2013 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 |
||||
* |
||||
* http://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.boot; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Test; |
||||
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.support.StaticApplicationContext; |
||||
|
||||
import static org.hamcrest.Matchers.instanceOf; |
||||
import static org.hamcrest.Matchers.is; |
||||
import static org.junit.Assert.assertThat; |
||||
import static org.mockito.Matchers.any; |
||||
import static org.mockito.Mockito.spy; |
||||
import static org.mockito.Mockito.verify; |
||||
|
||||
/** |
||||
* @author Dave Syer |
||||
*/ |
||||
public class SpringApplicationBuilderTests { |
||||
|
||||
private ConfigurableApplicationContext context; |
||||
|
||||
@After |
||||
public void close() { |
||||
if (this.context != null) { |
||||
this.context.close(); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void specificApplicationContextClass() throws Exception { |
||||
SpringApplicationBuilder application = new SpringApplicationBuilder().sources( |
||||
ExampleConfig.class).contextClass(StaticApplicationContext.class); |
||||
this.context = application.run(); |
||||
assertThat(this.context, is(instanceOf(StaticApplicationContext.class))); |
||||
} |
||||
|
||||
@Test |
||||
public void parentContextCreation() throws Exception { |
||||
SpringApplicationBuilder application = new SpringApplicationBuilder( |
||||
ChildConfig.class).contextClass(SpyApplicationContext.class); |
||||
application.parent(ExampleConfig.class); |
||||
this.context = application.run(); |
||||
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( |
||||
any(ApplicationContext.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void parentFirstCreation() throws Exception { |
||||
SpringApplicationBuilder application = new SpringApplicationBuilder( |
||||
ExampleConfig.class).child(ChildConfig.class); |
||||
application.contextClass(SpyApplicationContext.class); |
||||
this.context = application.run(); |
||||
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( |
||||
any(ApplicationContext.class)); |
||||
} |
||||
|
||||
@Test |
||||
public void parentContextIdentical() throws Exception { |
||||
SpringApplicationBuilder application = new SpringApplicationBuilder( |
||||
ExampleConfig.class); |
||||
application.parent(ExampleConfig.class); |
||||
application.contextClass(SpyApplicationContext.class); |
||||
this.context = application.run(); |
||||
verify(((SpyApplicationContext) this.context).getApplicationContext()).setParent( |
||||
any(ApplicationContext.class)); |
||||
} |
||||
|
||||
@Configuration |
||||
static class ExampleConfig { |
||||
|
||||
} |
||||
|
||||
@Configuration |
||||
static class ChildConfig { |
||||
|
||||
} |
||||
|
||||
public static class SpyApplicationContext extends AnnotationConfigApplicationContext { |
||||
|
||||
ConfigurableApplicationContext applicationContext = spy(new AnnotationConfigApplicationContext()); |
||||
|
||||
@Override |
||||
public void setParent(ApplicationContext parent) { |
||||
this.applicationContext.setParent(parent); |
||||
} |
||||
|
||||
public ConfigurableApplicationContext getApplicationContext() { |
||||
return this.applicationContext; |
||||
} |
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue