SpringBoot 程序启动入口
@SpringBootApplication
public class Springboot01DemoApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01DemoApplication.class, args);
}
}
点击SpringApplication.run查看源码
/**
* 运行 Spring 应用
*
* 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}
*
*/
//调用静态类,参数对应的就是SpringbootDemoApplication.class以及main方法中的args
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
/**
* 运行 Spring 应用
*
* 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) {
//SpringApplication的启动由两部分组成:
//1. 实例化SpringApplication对象
//2. run(args):调用run方法
return new SpringApplication(primarySources).run(args);
}
- 上述代码执行了两个操作:首先 new SpringApplication(primarySources), 执行run方法
new SpringApplication(primarySources),SpringApplication实例化的创建过程
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//项目启动类 SpringbootDemoApplication.class设置为属性存储起来
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置初始化器(Initializer),最后会调用这些初始化器
//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器(Listener)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
- 从上述代码中可以看出SpringApplication的初始化过程主要分为四个部分,具体代码可往下看
- this.webApplicationType = WebApplicationType.deduceFromClasspath(),主要作用是设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
- this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)),设置初始化器(Initializer),在初始化器设置过程中,会使用SpringFactoriesLoader从META-INF/spring.factories文件获取所有可用的应用初始化器类ApplicationContextInitializer
- this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)),设置监听器(Listener),与上面的设置初始化器一样,也是使用SpringFactoriesLoader从META-INF/spring.factories文件获取所有可用的监听器类ApplicationListener
- this.mainApplicationClass = this.deduceMainApplicationClass(),用于推断并设置项目main()方法启动的主程序启动类
WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {
// WebApplicationType.REACTIVE 类型 通过类加载器判断REACTIVE相关的Class是否存在
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) // 判断REACTIVE相关的Class是否存在
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// WebApplicationType.NONE 类型
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) { // 不存在 Servlet 的类
return WebApplicationType.NONE;
}
}
// WebApplicationType.SERVLET 类型。可以这样的判断的原因是,引入 Spring MVC 时,是内嵌的 Web 应用,会引入 Servlet 类。
return WebApplicationType.SERVLET;
}
getSpringFactoriesInstances(ApplicationContextInitializer.class)
/**
* 获得指定类类对应的对象们。
*
* @param type 指定类
* @param <T> 泛型
* @return 对象们
* 这里的入参type是:org.springframework.context.ApplicationContextInitializer.class
*/
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 = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组
// type = org.springframework.context.ApplicationContextInitializer.class
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
//org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
// 根据names来进行实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 对实例进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
/**
* 创建对象的数组
*
* @param type 父类
* @param parameterTypes 构造方法的参数类型
* @param classLoader 类加载器
* @param args 参数
* @param names 类名的数组
* @param <T> 泛型
* @return 对象的数组
*/
@SuppressWarnings("unchecked")
// parameterTypes: 上一步得到的names集合
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size()); // 数组大小,细节~
// 遍历 names 数组
for (String name : names) {
try {
// 获得 name 对应的类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 确认被加载类是ApplicationContextInitializer的子类
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;
}
getSpringFactoriesInstances(ApplicationListener.class), 同上,只有入参不一样
run方法执行原理,代码及注释如下
/**
* 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 主要用于简单统计 run 启动过程的时长。
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 属性
configureHeadlessProperty();
// (1)获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 创建 ApplicationArguments 对象 初始化默认应用参数类
// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//(2)项目运行环境Environment的预配置
// 创建并配置当前SpringBoot应用将要使用的Environment
// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// (3)创建Spring容器
context = createApplicationContext();
// 获得异常报告器 SpringBootExceptionReporter 数组
//这一步的逻辑和实例化初始化器和监听器的一样,
// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// (4)Spring容器前置处理
//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// (5):刷新容器
refreshContext(context);
// (6):Spring容器后置处理
//扩展接口,设计模式中的模板方法,默认为空实现。
// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
afterRefresh(context, applicationArguments);
// 停止 StopWatch 统计时长
stopWatch.stop();
// 打印 Spring Boot 启动的时长日志。
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// (7)发出结束执行的事件通知
listeners.started(context);
// (8):执行Runners
//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
// (9)发布应用上下文就绪事件
//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
// 这样整个Spring Boot项目就正式启动完成了。
try {
listeners.running(context);
} catch (Throwable ex) {
// 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//返回容器
return context;
}
- 从上述代码中可以看出总共有9个步骤
- getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 这里仍然利用了getSpringFactoriesInstances方法来获取实例,
// 从META-INF/spring.factories中读取Key为org.springframework.boot.SpringApplicationRunListener的Values
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
2. prepareEnvironment
/**
* 加载外部化配置资源到environment,包括命令行参数、servletConfigInitParams、
* servletContextInitParams、systemProperties、sytemEnvironment、random、
* application.yml(.yaml/.xml/.properties)等;初始化日志系统。
* @param listeners
* @param applicationArguments
* @return
*/
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//获取或创建环境(存在就直接返回,不存在创建一个再返回)
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境:配置PropertySources和active Profiles
configureEnvironment(environment, applicationArguments.getSourceArgs());
//listeners环境准备(就是广播ApplicationEnvironmentPreparedEvent事件)。
listeners.environmentPrepared(environment);
//将环境绑定到SpringApplication
bindToSpringApplication(environment);
//如果非web环境,将环境转换成StandardEnvironment
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 配置PropertySources对它自己的递归依赖
// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
ConfigurationPropertySources.attach(environment);
return environment;
}
/**
* @return 获得或创建 ConfigurableEnvironment 对象
*/
private ConfigurableEnvironment getOrCreateEnvironment() {
// 已经存在,则进行返回
if (this.environment != null) {
return this.environment;
}
// 不存在,则根据 webApplicationType 类型,进行创建。
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
- createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
// 根据 webApplicationType 类型,获得 ApplicationContext 类型
// 这里创建容器的类型 还是根据webApplicationType进行判断的,
// 该类型为SERVLET类型,所以会通过反射装载对应的字节码,
// 也就是AnnotationConfigServletWebServerApplicationContext
// 先判断有没有指定的实现类
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
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);
}
}
// 创建 ApplicationContext 对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
通过上述代码可以看出创建的是AnnotationConfigServletWebServerApplicationContext
- prepareContext
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置容器环境,包括各种变量
context.setEnvironment(environment);
//设置上下文的 bean 生成器和资源加载器
postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer(包括 spring.factories和自定义的实例)
applyInitializers(context);
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
// 加载所有资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载我们的启动类,将启动类注入容器,为后续开启自动化配置奠定基础
load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
//这块会对整个上下文进行一个预处理,比如触发监听器的响应事件、加载资源、设置上下文环境等等
}
- refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
// 开启(刷新)Spring 容器,通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
refresh(context);
// 注册 ShutdownHook 钩子
if (this.registerShutdownHook) {
try {
//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
// 断言,判断 applicationContext 是 AbstractApplicationContext 的子类
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
// 启动(刷新) AbstractApplicationContext
((AbstractApplicationContext) applicationContext).refresh();
}
从上面创建ApplicationContext过程可以知道refresh执行的是AnnotationConfigServletWebServerApplicationContext中的方法,代码如下
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
- afterRefresh,空方法,自己扩展
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
//该方法没有实现,可以根据需要做一些定制化的操作。
}
- listeners.started
public void started(ConfigurableApplicationContext context) {
//执行所有SpringApplicationRunListener实现的started方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
- callRunners
Spring Boot提供的执行器接口有ApplicationRunner 和CommandLineRunner两种,在使用时只需要自定义一个执行器实现其中一个接口并重写对应的run()方法,然后Spring Boot项目启动后会立即执行这些方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
// 获得所有 Runner 们
List<Object> runners = new ArrayList<>();
// 获得所有 ApplicationRunner 实现类
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// 获得所有 CommandLineRunner 实现类
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// 排序 runners
AnnotationAwareOrderComparator.sort(runners);
// 遍历 Runner 数组,执行逻辑
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);
}
}
从上面的代码可以看出 ApplicationRunner优先CommandLineRunner执行
- listeners.running
public void running(ConfigurableApplicationContext context) {
//触发所有 SpringApplicationRunListener 监听器的 running 事件方法。
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
整体流程大概如下