SpringBoot启动过程:SpringApplication及SpringApplicationBuilder
Posted 恒奇恒毅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot启动过程:SpringApplication及SpringApplicationBuilder相关的知识,希望对你有一定的参考价值。
SpringBoot应用的启动是基于SpringApplication
类的,下面一步步地分析。
构造方法及初始化
其构造方法有二,都调用了initialize方法,完成注解源的配置。
public SpringApplication(Object... sources)
initialize(sources);
public SpringApplication(ResourceLoader resourceLoader, Object... sources)
this.resourceLoader = resourceLoader;
initialize(sources);
private void initialize(Object[] sources)
if (sources != null && sources.length > 0)
this.sources.addAll(Arrays.asList(sources));
this.webEnvironment = deduceWebEnvironment();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
- initialize方法首先将注解源设置到成员变量
this.sources
中,然后deduceWebEnvironment方法中根据类路径是否有javax.servlet.Servlet、org.springframework.web.context.ConfigurableWebApplicationContext
这两个类判断是否是web环境this.webEnvironment
。 - 利用spring.factories机制机制加载
ApplicationContextInitializer
和ApplicationListener
。 - 然后找到main方法所在类
mainApplicationClass
。
run方法
run方法是程序运行的主要方法
public ConfigurableApplicationContext run(String... args)
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo)
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
return context;
catch (Throwable ex)
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
SpringApplicationRunListeners
加载,利用spring.factories机制加载SpringApplicationRunListener
,封装到SpringApplicationRunListeners
,SpringApplicationRunListener
定义了一些容器的生命周期方法,SpringApplicationRunListeners
就是为了方便循环调用多个SpringApplicationRunListener
的方法。在容器的各个生命周期时刻调用相应的方法。
public interface SpringApplicationRunListener
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* @link ApplicationContext has been created.
* @param environment the environment
*/
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
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes.
* @param context the application context or null if a failure occurred before the
* context was created
* @param exception any run exception or null if run completed successfully.
*/
void finished(ConfigurableApplicationContext context, Throwable exception);
- 将主方法入参args封装为
ApplicationArguments
(默认实现DefaultApplicationArguments
),方便获取命令行参数
public interface ApplicationArguments
/**
* Return the raw unprocessed arguments that were passed to the application.
* @return the arguments
*/
String[] getSourceArgs();
/**
* Return then names of all option arguments. For example, if the arguments were
* "--foo=bar --debug" would return the values @code ["foo", "debug"].
* @return the option names or an empty set
*/
Set<String> getOptionNames();
/**
* Return whether the set of option arguments parsed from the arguments contains an
* option with the given name.
* @param name the name to check
* @return @code true if the arguments contain an option with the given name
*/
boolean containsOption(String name);
/**
* Return the collection of values associated with the arguments option having the
* given name.
* <ul>
* <li>if the option is present and has no argument (e.g.: "--foo"), return an empty
* collection (@code [])</li>
* <li>if the option is present and has a single value (e.g. "--foo=bar"), return a
* collection having one element (@code ["bar"])</li>
* <li>if the option is present and has multiple values (e.g. "--foo=bar --foo=baz"),
* return a collection having elements for each value (@code ["bar", "baz"])</li>
* <li>if the option is not present, return @code null</li>
* </ul>
* @param name the name of the option
* @return a list of option values for the given name
*/
List<String> getOptionValues(String name);
/**
* Return the collection of non-option arguments parsed.
* @return the non-option arguments or an empty list
*/
List<String> getNonOptionArgs();
- 准备
ConfigurableEnvironment
,其中configureEnvironment将命令行参数封装为SimpleCommandLinePropertySource
添加到环境中,配置profile。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments)
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
if (!this.webEnvironment)
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
return environment;
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args)
configurePropertySources(environment, args);
configureProfiles(environment, args);
/**
* Add, remove or re-order any @link PropertySources 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)
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty())
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
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(
name + "-" + args.hashCode(), 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)
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
createApplicationContext
方法创建容器ConfigurableApplicationContext
,根据签名的是否web环境决定是创建AnnotationConfigEmbeddedWebApplicationContext
或者AnnotationConfigApplicationContext
- 创建失败分析器
FailureAnalyzers
prepareContext
准备环境
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner)
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo)
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null)
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
// Load the sources
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
- 给容器设置环境
postProcessApplicationContext
:beanNameGenerator和resourceLoaderapplyInitializers
应用容器初始化器ApplicationContextInitializer
ApplicationArguments
放到容器中Banner
放到容器中- load方法利用
BeanDefinitionLoader
加载注解源,此时容器就注册好了所有的注解源相关的BeanDefinition
refreshContext
刷新容器,这一步是最重要的进行了容器的刷新,并且注册了ShutdownHook
afterRefresh
方法中完成所有runner的执行,包括ApplicationRunner
和CommandLineRunner
。- 至此完成容器的创建和初始化
SpringApplicationBuilder
该类提供了配置SpringApplication
的快捷方法。在外置容器创建及启动ApplicationContext过程中,我们自定义的SpringBootServletInitializer
就可以利用SpringApplicationBuilder
定制容器。
以上是关于SpringBoot启动过程:SpringApplication及SpringApplicationBuilder的主要内容,如果未能解决你的问题,请参考以下文章