@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
这是一个标准的SpringBoot应用。main方法中调用了org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)方法,参数是当前类和main方法传入的参数。
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified source using default settings.
* @param primarySource the primary source to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/**
* Static helper that can be used to run a {@link SpringApplication} from the
* specified sources using default settings and user supplied arguments.
* @param primarySources the primary sources to load
* @param args the application arguments (usually passed from a Java main method)
* @return the running {@link ApplicationContext}
*/
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
通过查看该方法我们可以看到,实际的调用是创建了一个SpringApplication实例,接着调用该实例的run方法。接下来让我们来看看创建SpringApplication都做了哪些操作。
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #SpringApplication(ResourceLoader, Class...)
* @see #setSources(Set)
*/
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 判断应用类型,普通应用 web应用 和 react-web应用
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication的构造方法主要做了一些初始化操作
- 对实例变量中的资源加载器进行赋值
- 判断主配置是否为空
- 存储主配置到实例变量中去,以便后续使用
- 判断应用类型,并存储到webApplicationType字段中去
- 初始化Bootstraper的实现类,存储到bootstrappers字段中去(主要通过spring.factories配置文件要加载,用于定制BootstrapContext上下文,存储一些ApplicationContext初始化完成前的一些实例对象)
- 设置ApplicationContextInitializer的实现类,用于定制ApplicationContext,加载方式同Bootstraper
- 设置ApplicationListener的实现类,Spring监听器,加载方式同上
- 最后对main方法入口类进行判断
让我们来看看SpringBoot是如何对应用的类型进行判断的
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
从代码中我们可以看到,通过应用的ClassPath中是否存在某几个类来判断具体应用类型。我们当前的应用没有引用任何的Web包,所以判断下来应该是WebApplicationType.NONE类型。
org.springframework.util.ClassUtils#isPresent判断某个类是否存在,主要通过java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)的方式来判断,若能没有抛出异常则判定该类是存在的,当然Spring在判断的健壮性上做了很多逻辑这里就不详细展开了,有兴趣的同学可以自己去看下源码。
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
} catch (IllegalAccessError var3) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
} catch (Throwable var4) {
return false;
}
}
public static Class<?> forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
clazz = (Class)commonClassCache.get(name);
}
if (clazz != null) {
return clazz;
} else {
Class elementClass;
String elementName;
if (name.endsWith("[]")) {
elementName = name.substring(0, name.length() - "[]".length());
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith("[L") && name.endsWith(";")) {
elementName = name.substring("[L".length(), name.length() - 1);
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else if (name.startsWith("[")) {
elementName = name.substring("[".length());
elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
} else {
ClassLoader clToUse = classLoader;
if (classLoader == null) {
clToUse = getDefaultClassLoader();
}
try {
return Class.forName(name, false, clToUse);
} catch (ClassNotFoundException var9) {
int lastDotIndex = name.lastIndexOf(46);
if (lastDotIndex != -1) {
String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
try {
return Class.forName(innerClassName, false, clToUse);
} catch (ClassNotFoundException var8) {
}
}
throw var9;
}
}
}
}
接下来我们来看下Bootstrapper。
/**
* Callback interface that can be used to initialize a {@link BootstrapRegistry} before it
* is used.
*
* @author Phillip Webb
* @since 2.4.0
* @see SpringApplication#addBootstrapper(Bootstrapper)
* @see BootstrapRegistry
*/
public interface Bootstrapper {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void intitialize(BootstrapRegistry registry);
}
/**
* A simple object registry that is available during startup and {@link Environment}
* post-processing up to the point that the {@link ApplicationContext} is prepared.
* <p>
* Can be used to register instances that may be expensive to create, or need to be shared
* before the {@link ApplicationContext} is available.
* <p>
* The registry uses {@link Class} as a key, meaning that only a single instance of a
* given type can be stored.
* <p>
* The {@link #addCloseListener(ApplicationListener)} method can be used to add a listener
* that can perform actions when {@link BootstrapContext} has been closed and the
* {@link ApplicationContext} is fully prepared. For example, an instance may choose to
* register itself as a regular Spring bean so that it is available for the application to
* use.
*
* @author Phillip Webb
* @since 2.4.0
* @see BootstrapContext
* @see ConfigurableBootstrapContext
*/
public interface BootstrapRegistry {
// ...略过该接口定义的方法
}
从这两个接口的注释中我们可以看出来,Bootstrapper的实现类主要用于对BootstrapRegistry实例进行一些初始化定制,而BootstrapRegistry主要用于在ApplicationContext初始化完成前注册一些实例来使用,并且可以添加一个监听器来监听BootstrapContext(我理解为应用启动期间的启动上下文环境)上下文关闭的事件,用于对BootstrapContext关闭时做一些操作,如将一些BootstrapContext期间注册的一些实例注册到ApplicationContext中去。
public class DemoBootstrapper implements Bootstrapper {
@Override
public void intitialize(BootstrapRegistry registry) {
registry.register(DemoBean.class, context -> {
DemoBean demoBean = new DemoBean();
demoBean.setText("demo");
return demoBean;
});
registry.addCloseListener(event -> {
BootstrapContext bootstrapContext = event.getBootstrapContext();
ApplicationContext applicationContext = event.getApplicationContext();
DemoBean demoBean = bootstrapContext.get(DemoBean.class);
((ConfigurableApplicationContext)applicationContext).getBeanFactory().registerSingleton("demoBean", demoBean);
});
}
}
// META-INF/spring.factories
org.springframework.boot.Bootstrapper=com.example.demo.bootstrap.DemoBootstrapper
如上面demo中所示的代码,就会在BootstrapContext中注册一个DemoBean,并且在BootstrapContext关闭时将DemoBean实例注册到ApplicationContext中去。SpringBoot如此设计为开发者提供了极高的可扩展性,如SpringCloud中就利用了该特性在应用初始化前做了一些拓展。
/**
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
*
* <p>Typically used within web applications that require some programmatic initialization
* of the application context. For example, registering property sources or activating
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
*
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
* implemented or if the {@link org.springframework.core.annotation.Order @Order}
* annotation is present and to sort instances accordingly if so prior to invocation.
*
* @author Chris Beams
* @since 3.1
* @param <C> the application context type
* @see org.springframework.web.context.ContextLoader#customizeContext
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
*/
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
ApplicationContextInitializer提供对ApplicationContext的初始化定制,在ApplicationContext#refresh之前。让我们看下面这个典型的例子,就能明白该接口可用于怎样的扩展了。
/**
* {@link ApplicationContextInitializer} for setting the servlet context.
*
* @author Dave Syer
* @author Phillip Webb
* @since 2.0.0
*/
public class ServletContextApplicationContextInitializer
implements ApplicationContextInitializer<ConfigurableWebApplicationContext>, Ordered {
private int order = Ordered.HIGHEST_PRECEDENCE;
private final ServletContext servletContext;
private final boolean addApplicationContextAttribute;
/**
* Create a new {@link ServletContextApplicationContextInitializer} instance.
* @param servletContext the servlet that should be ultimately set.
*/
public ServletContextApplicationContextInitializer(ServletContext servletContext) {
this(servletContext, false);
}
/**
* Create a new {@link ServletContextApplicationContextInitializer} instance.
* @param servletContext the servlet that should be ultimately set.
* @param addApplicationContextAttribute if the {@link ApplicationContext} should be
* stored as an attribute in the {@link ServletContext}
* @since 1.3.4
*/
public ServletContextApplicationContextInitializer(ServletContext servletContext,
boolean addApplicationContextAttribute) {
this.servletContext = servletContext;
this.addApplicationContextAttribute = addApplicationContextAttribute;
}
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);
if (this.addApplicationContextAttribute) {
this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
applicationContext);
}
}
}
ServletContextApplicationContextInitializer将servletContext存储到ApplicationContext的实例变量中,并且如果addApplicationContextAttribute为true,会将整个ApplicationContext置入到servletContext中。
ApplicationListener想必大家都已经比较熟悉了,这里就不展开叙述了。
接下来让我们看看SpringBoot在org.springframework.boot.SpringApplication#run(java.lang.String...)方法具体做了哪些操作
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// 应用初始化计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建一个初始化上下文环境
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置Headless模式,在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式。
configureHeadlessProperty();
// 加载SpringApplicationRunListener监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 封装main方法传入的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建Spring环境信息ConfigurableEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印SpringBoot Banner
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 配置ApplicationContext
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新ApplicationContext
refreshContext(context);
// 刷新后的一些处理
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 通知SpringApplicationRunListener ApplicationContext启动完成
listeners.started(context);
// 执行Runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 通知SpringApplicationRunListener ApplicationContext运行中
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
从代码上来看整体逻辑还是比较清晰的,方法调用上基本都能见名知意,就让我们来看几个关键的方法。
先来看看DefaultBootstrapContext bootstrapContext = createBootstrapContext();
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
比较简单,就创建了一个DefaultBootstrapContext对象,然后循环调用SpringApplication构造方法中加载并存储的Bootstrapper实现类。
接下来我们来看下SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
SpringApplicationRunListeners 是一个封装对象,用于封装SpringApplicationRunListener集合,采用面向对象的思想,隐藏了多个SpringApplicationRunListener循环监听某个事件的具体细节,使代码更加清晰,可扩展性更高。
/**
* Listener for the {@link SpringApplication} {@code run} method.
* {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
* and should declare a public constructor that accepts a {@link SpringApplication}
* instance and a {@code String[]} of arguments. A new
* {@link SpringApplicationRunListener} instance will be created for each run.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.0.0
*/
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
* @param bootstrapContext the bootstrap context
*/
default void starting(ConfigurableBootstrapContext bootstrapContext) {
starting();
}
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
* @deprecated since 2.4.0 in favor of {@link #starting(ConfigurableBootstrapContext)}
*/
@Deprecated
default void starting() {
}
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param bootstrapContext the bootstrap context
* @param environment the environment
*/
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
environmentPrepared(environment);
}
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
* @deprecated since 2.4.0 in favor of
* {@link #environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)}
*/
@Deprecated
default void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
default void contextPrepared(ConfigurableApplicationContext context) {
}
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
default void running(ConfigurableApplicationContext context) {
}
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
SpringApplicationRunListener定义了SpringApplication各个生命周期的监听方法,如开始运行、环境加载完毕、上下文创建等。EventPublishingRunListener就是一个实现该接口来完成广播Spring事件的实现类,后面会对该类进行详细的解析。
所以listeners.starting(bootstrapContext, this.mainApplicationClass);方法的作用就比较明了了。
private final List<SpringApplicationRunListener> listeners;
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
最主要的就是这段代码,this.listeners.forEach(listenerAction),循环调用SpringApplicationRunListener的starting方法。
这里引入了一个类ApplicationStartup,主要用于在应用程序启动期间标记步骤,并收集有关执行上下文或其处理时间的数据的,这里就不展开叙述了。
接下来我们来看下一个比较关键的方法,ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 创建ConfigurableEnvironment实例
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
// 根据前面检测的webApplicationType判断创建哪种环境
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
getOrCreateEnvironment();比较简单,主要根据之前检测的webApplicationType来判断创建哪种类型的Environment,我们这里的话创建得到是StandardEnvironment。
我们着重来看下configureEnvironment(environment, applicationArguments.getSourceArgs());都做了些什么。
/**
* Template method delegating to
* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
* Override this method for complete control over Environment customization, or one of
* the above for fine-grained control over property sources or profiles, respectively.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureProfiles(ConfigurableEnvironment, String[])
* @see #configurePropertySources(ConfigurableEnvironment, String[])
*/
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 设置ConversionService
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
/**
* Add, remove or re-order any {@link PropertySource}s in this application's
* environment.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
*/
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
// 添加SpringApplication中的defaultProperties
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
// 如果需要添加命令参数到环境中并且命令参数(main方法传入的参数)个数大于0
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
/**
* Configure which profiles are active (or active by default) for this application
* environment. Additional profiles may be activated during configuration file
* processing via the {@code spring.profiles.active} property.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
*/
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
// 将SpringApplication传入的additionalProfiles置入Environment
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
主要做了如下几个操作:
- 将设置的defaultProperties置入Environment。(我们可以通过org.springframework.boot.SpringApplication#setDefaultProperties(java.util.Map<java.lang.String,java.lang.Object>)或者org.springframework.boot.SpringApplication#setDefaultProperties(java.util.Properties)传入该字段)
- 将命令参数(main方法传入的参数)置入Environment。如果已经存在了,则进行合并置入。
- 将additionalProfiles置入到Environment的ActiveProfiles列表中。(我们可以通过org.springframework.boot.SpringApplication#setAdditionalProfiles传入该字段)
接下来逻辑就比较简单了,主要对Environment进行挂载和通知org.springframework.boot.SpringApplicationRunListeners环境准备完毕。