1. 一个简单SpringBoot项目主启动类配置
@SpringBootApplication
public class SpringBoot05MongodbApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot05MongodbApplication.class, args);
}
}
2. SpringApplication#run方法总体流程
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
2.1 SpringApplication构造方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@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));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从spring.factories文件中获取应用初始化器 通过反射获取并注入当前Boot应用中
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从spring.factories文件中获取应用监听器 通过反射获取并注入当前Boot应用中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推导主启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication#getSpringFactoriesInstances方法追踪:
/** 获取定义在spring.factories文件中的组件 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
/* 使用反射机制生成spring.factories文件中的组件 */
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
} catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
SpringFactoriesLoader.loadFactoryNames方法详情:解析spring.factories文件中数据读入Properties中
![]()
SpringApplication#deduceMainApplicationClass方法追踪:获取当前运行java方法栈中名称为main的方法对应的类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
} catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
PS: spring.factories文件一般由spring-boot-autoconfigure 子模块中的WEB-INF目录下,是一种SPI机制的实现方式,spring-boot-autoconfigure是SpringBoot实现自动装配的基础,同时也是spring-boot-starter的依赖,间接属于各个starter的简介依赖
![]()
2.2 SpringApplication#run方法总览
/**
* Run the Spring application, creating and refreshing a new {@link ApplicationContext}.
* -- 启动Spring应用,创建并且刷新ApplicationContext
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
/** 1. 创建一个 StopWatch,允许计时的一些任务, 公开每个命名任务的总运行时间和运行时间启动任务计时器 */
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 存放异常报告类
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
/**
* 2. 配置java.awt.headless为true -- 模式切换
* 怎么从正常模式切换到headline模式呢, 其实很简单,java提供了几种方式:
* 1、在main函数入口通过System.setProperties("java.awt.headless", true);
* 2、通过java的启动参数指定 JAVA_OPTS="-Djava.awt.headless=true"
* 3、在环境变量中定义改属性,并且赋值位true。 export java.awt.headless=true
* 使用以上3种方法都可以让jvm在启动的时候,把工作模式修改位headline,这样就不会在创建使用awt资源的
* 时候去加载window交互系统提供的具体实现了
*/
configureHeadlessProperty();
/**
* 3. 从当前classpath下获取所有的META-INF/spring.factories文件获取所有的SpringApplicationRunListener——Spring运行监听器
* # Run Listeners
* org.springframework.boot.SpringApplicationRunListener=\
* org.springframework.boot.context.event.EventPublishingRunListener
* 同时将运行监听器启动(默认配置使用EventPublishingRunListener),该方法发布一个ApplicationStartingEvent事件
*/
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
/*4. 创建一个默认的为SpringBoot应用启动提供获取参数的功能组件*/
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
/**
* 5. 根据当前SpringBoot的web环境状态创建一个符合的Environment
* Servlet 的Web环境下使用StandardServletEnvironment
* REACTIVE 的Web环境下使用StandardReactiveWebEnvironment
* 非Web环境下使用StandardEnvironment
* 同时使用applicationArguments获取资源信息
*/
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 6. 根据运行时环境数据spring.beaninfo.ignore值 配置是否需要忽略某些Bean
configureIgnoreBeanInfo(environment);
// 7. 打印Banner展示
Banner printedBanner = printBanner(environment);
/**
* 8. 创建Spring ApplicationContext上下文
* SERVLET使用 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
* REACTIVE使用 org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
* 默认使用 org.springframework.context.annotation.AnnotationConfigApplicationContext
* 使用反射机制生成类对应的对象
*/
context = createApplicationContext();
/**
* 9. 从当前classpath下获取所有的META-INF/spring.factories文件
* 从文件获取所有的SpringBootExceptionReporter Spring运行异常报告器
*# Error Reporters
* org.springframework.boot.SpringBootExceptionReporter=\
* org.springframework.boot.diagnostics.FailureAnalyzers
*/
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
/* 10. 预准备Spring ApplicationContext上下文 */
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
/* 11. 核心方法 ApplicationContext#refresh方法相关*/
refreshContext(context);
/* 12. IOC容器refresh之后的操作,目前该方法是空实现 */
afterRefresh(context, applicationArguments);
// 13. 停止监控器
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
/* 14. 发布IOC容器已经成功启动的事件 */
listeners.started(context);
/*15. 指定自定义的CommandLineRunner ApplicationRunner*/
callRunners(context, applicationArguments);
} catch (Throwable ex) {
/*16. 处理异常信息 */
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
2.3 SpringApplication#run方法拆解
1. 创建一个 StopWatch,允许计时的一些任务, 公开每个命名任务的总运行时间和运行时间启动任务计时器
public StopWatch() { this(""); }
public StopWatch(String id) {
this.keepTaskList = true;
this.taskList = new LinkedList();
this.id = id;
}
public void start() throws IllegalStateException { this.start(""); }
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
}
2. 配置java.awt.headless为true -- 模式切换
/* 配置java.awt.headless,默认为true */
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3. 从当前classpath下获取所有的META-INF/spring.factories文件获取所有的SpringApplicationRunListener——Spring运行监听器
/* 获取启动监听器 */
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
4. 创建一个默认的为SpringBoot应用启动提供获取参数的功能组件
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
5. 根据当前SpringBoot的web环境状态创建一个符合的Environment
/* 预准备SpringBoot运行环境 */
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置运行时环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 运行监听器中注入运行环境
listeners.environmentPrepared(environment);
// 将运行环境绑定在SpringBoot上下文中
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
// 从运行环境中获取配置资源附件
ConfigurationPropertySources.attach(environment);
return environment;
}
getOrCreateEnvironment方法追踪
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) { return this.environment; }
switch (this.webApplicationType) {
case SERVLET:
// Servlet 的Web环境下使用StandardServletEnvironment
return new StandardServletEnvironment();
case REACTIVE:
// REACTIVE 的Web环境下使用StandardReactiveWebEnvironment
return new StandardReactiveWebEnvironment();
default:
// 非Web环境下使用StandardEnvironment
return new StandardEnvironment();
}
}
configureEnvironment方法追踪
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
/**
* Add, remove or re-order any {@link PropertySource}s in this application's
* environment.
* -- 在此应用程序中添加、删除或重新排序任何{@link PropertySource} 环境
* @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("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) {
environment.getActiveProfiles(); // ensure they are initialized 获取当前运行获取所有处于活跃状态的Profile
// But these ones should go first (last wins in a property key clash) 进行去重操作
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
bindToSpringApplication方法追踪
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
} catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
ConfigurationPropertySources.attach方法追踪
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
6. 根据运行时环境数据spring.beaninfo.ignore值 配置是否需要忽略某些Bean
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
7. 打印Banner展示
private Banner printBanner(ConfigurableEnvironment environment) {
// 如果Banner模式使用关闭则不需要打印
if (this.bannerMode == Banner.Mode.OFF) { return null; }
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
// 使用日志模式则输出在日志中
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
// 默认打印输出在console控制台中
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
8. 创建Spring ApplicationContext上下文
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
// 默认使用 org.springframework.context.annotation.AnnotationConfigApplicationContext
// SERVLET使用 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
// REACTIVE使用 org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
9. 从当前classpath下获取所有的META-INF/spring.factories文件,从文件获取所有的SpringBootExceptionReporter Spring运行异常报告器
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
10. 预准备Spring ApplicationContext上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
/**
* 为当前ApplicationContext注入
* environment ---- 运行时环境
* beanNameGenerator ---- Bean名称生成器
* resourceLoader ---- 资源加载器
* classLoader ---- 类加载器
*/
context.setEnvironment(environment);
postProcessApplicationContext(context);
/**
* 构造器方法从当前classpath下获取所有的META-INF/spring.factories文件,获取ApplicationContextInitializer对应数据解析成 this.initializers
* # Application Context Initializers
* org.springframework.context.ApplicationContextInitializer=\
* org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
* org.springframework.boot.context.ContextIdApplicationContextInitializer,\
* org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
* org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
* 此处执行该this.initializers中的所有初始化方法
* 运行时监控器发布上下文已准备事件
*/
applyInitializers(context);
listeners.contextPrepared(context);
// 如果记录启动日志的话记录启动+当前活跃Profile信息
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans 添加引导特定的单例bean
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
/**
* Load the sources 获取所有资源配置
* 将所有Bean定义加载到ApplicationContext上下文中
* 运行时监控器发布上下文已加载事件
*/
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
11. 核心方法 ApplicationContext#refresh方法相关
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
AbstractApplicationContext.refresh()单独解析流程
12. IOC容器refresh之后的操作,目前该方法是空实现
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }
13. 停止监控器
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
} else {
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new StopWatch.TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
}
14. 发布IOC容器成功启动的事件
15. 指定自定义的CommandLineRunner ApplicationRunner
/* 调用所有自定义的ApplicationRunner+CommandLineRunner 执行对应的业务方法*/
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
} catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
16. 启动异常处理
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
/* 处理退出码 */
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
/*报告错误 */
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
/* 记录异常错误信息*/
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
try {
for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
registerLoggedException(failure);
return;
}
}
} catch (Throwable ex) {
// Continue with normal handling of the original failure
}
if (logger.isErrorEnabled()) {
logger.error("Application run failed", failure);
registerLoggedException(failure);
}
}
protected void registerLoggedException(Throwable exception) {
SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
if (handler != null) {
handler.registerLoggedException(exception);
}
}
/* 根据应用退出码处理 */
private void handleExitCode(ConfigurableApplicationContext context, Throwable exception) {
int exitCode = getExitCodeFromException(context, exception);
if (exitCode != 0) {
if (context != null) {
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
SpringBootExceptionHandler handler = getSpringBootExceptionHandler();
if (handler != null) {
handler.registerExitCode(exitCode);
}
}
}