写在前面
在Coding的这些日子里,自己总是遇到问题之后才去学习,在解决问题以后,又充满慈悲的把知识放生。除了一些业务解题以外,自己留下的知识比较少。一直以来自己很想阅读源码,但都是不了了之。总结一下除了业务太多、自己懒惰以外,还因为是没有找到很好的学习方式。
首先,我是一个比较浮躁的人,做事喜欢能够立竿见影的得到结果。所以在之前的阅读中,总是发现读了好多,什么也没有沉淀,就气急败坏,但源码阅读(知识学习)从来都不是一蹴而就的事情。急功近利只会一败涂地。
其次,我的学习是深度优先,所以就会在一个点上深入,到最后阅读半天发现还在前几行中,一天下来觉得进度寥寥。但源码的阅读不能这样,只有先有了全局意识,才能更加了解局部。构建脉络 --> 丰富细节!
最后,这篇文章仅作为自己源码学习的一个记录,如有不对请不吝指正!感谢大佬~
SpringBoot
什么是SpringBoot?
**Build Anything with Spring Boot:**Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring.
SpringBoot是Spring应用的起点。它更多的是一种设计理念和规则标准。它将很多东西封装起来,我们可以应用各种各样的starter,就可以拆包即用。
SpringBoot的好处是什么?
- 简化了Spring应用的开发难度;
- 有大量的默认配置,使得可以无配置集成主流框架,减少了很多SSM搭建中的繁琐过程
- web.xml
- 数据库配置xml,配置日志文件
- ...
SpringBoot应用如何启动?
// 该注解标识此类为项目的启动类,打完JAR包以后,去执行时,会执行此类中的main方法
@SpringBootApplication
public class WorkToolsApplication {
public static void main(String[] args) {
// SpringApplication.run方法是程序的入口
SpringApplication.run(WorkToolsApplication.class, args);
}
}
@SpringBootApplication:标记此类是项目的启动类,只需要执行这个注解标记的类中的main方法即可启动类;
SpringApplication.run():run方法是一个静态方法,主要的作用是返回一个SpringApplicationContext(理解为一个应用容器)
run方法
// 首先进入次方法,官方解释:静态助手,可以使用默认设置从指定的源运行一个SpringApplication
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);
}
根据上面的代码,我们可以看出run方法只干了两件事:
- new了一个SpringApplication对象;
- 执行了SpringApplication对象的run方法,返回了一个可配置的应用上下文(或者说一个Spring应用);
生成一个Spring应用(new SpringApplication())
// primarySources 为传入的WorkToolsApplication.class
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
// 调用的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 根据调用方法得知,此处的resourceLoader 为NULL
this.resourceLoader = resourceLoader;
// 校验传入的primarySources是否为空,此处是WorkToolsApplication.class
Assert.notNull(primarySources, "PrimarySources must not be null");
// 将传入的class放入SpringApplication的成员变量中,使用地方待补充
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 获取应用的类型,并赋值给成员变量。获取方法后续讲解
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化引导程序
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断并设置主要应用的class
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication的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}
*/
// 官方解释为 运行一个Spring的应用程序,创建、刷新一个新的ApplicationContext
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 创建一个引导程序的上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 设置无头属性
configureHeadlessProperty();
// 获取运行的监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 不太理解
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建一个可配置的环境信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 在环境信息中配置可以忽视的bean的信息
configureIgnoreBeanInfo(environment);
// 横幅打印
Banner printedBanner = printBanner(environment);
// 创建一个应用程序的上下文
context = createApplicationContext();
// TODO
context.setApplicationStartup(this.applicationStartup);
// 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文,即调用对应程序的上下文的refresh方法
refreshContext(context);
// 刷新上下文以后的操作
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 找到所有的runner(实现ApplicationRunner,CommandLineRunner),并执行对应run方法。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 所有监听器runing起来,设置其上下文
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回上下文
return context;
}
其中比较重要的是:
- createBootstrapContext 创建引导程序上下文;
- getRunListeners 获取监听器;
- prepareEnvironment 准备环境信息;
- createApplicationContext 创建应用程序上下文;
- prepareContext 准备上下文;
- refreshContext 刷新上下文;
- callRunners 调用runner的方法;
总结
这个文章是入门的第一章,通过这篇文章的阅读,我们可以了解,大致上一个SpringApplication 的运行步骤;
- 利用@SpringBootApplication注解标记一个类,让类中main方法称为程序启动的钥匙;
- main方法使用SpringAPplication.run() 来初始化一个Spring应用,并设置它的上下文;
- new一个SpringApplication对象;
- 设置资源自动器resourceLoader;
- 设置主要的资源类,即传入的入口类的class;
- 设置应用的类型;
- 设置引导器;
- 初始化并设置初始化器;
- 获取并设置监听器实例;
- 设置主要的应用class;
- 执行这个对象的run方法;
- 创建引导器的上下文;
- 配置无头属性(暂时觉得不重要);
- 获取运行的监听器;
- 利用args创建一个applicationArguments对象;
- 使用监听器,引导程序上下文,applicationArguments去创建一个可配置的环境对象;
- 环境对象中配置可忽略的bean信息;
- 创建一个应用的上下文(最终要返回的上下文);
- 准备这个上下文,利用引导上下文、环境对象、监听器、应用参数等;
- 刷新上下文;
- 回调用户配置的Runners;
- 让监听器,以context运行起来;
- 返回这个context;
- new一个SpringApplication对象;
- 初始化并运行了一个SpringBoot容器;