本系列springboot源码都基于 2.2.5.RELEASE
入口类
1 |
|
一直跟进方法,这里将入口类当做参数,创建了一个SpringApplication
实例,并调用run(args)
方法。
1 | public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { |
继续跟进构造器
1 | public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { |
推断应用类型
deduceFromClasspath()
方法查找一些类是否存在来判断是reactive、还是servlet服务。
1 | static WebApplicationType deduceFromClasspath() { |
设置初始化器
跟踪方法,最后调用的方法是getSpringFactoriesInstances
,传入的参数 type 是ApplicationContextInitializer.class
。
1 | private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { |
跟进loadFactoryNames()
方法,它将ApplicationContextInitializer.class
的名字作为 key 去从 map 中取出想要的名字。
1 | public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { |
继续跟进
1 | public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; |
以 org.springframework.boot:spring-boot
包为例,它的META-INF/spring.factories
文件中包含以下内容:
1 | # Application Context Initializers |
那么,这五个类名就会被读出来放到Set<String> names
中,并开始准备下面的实例化操作。
1 | // 入参 type 是 ApplicationContextInitializer.class, names 是上面取出来的五个类名 |
设置监听器
我们跟进监听器,可以发现代码和上面设置初始化器的流程一模一样,这里入参变成了ApplicationListener.class
。再查看一下 org.springframework.boot:spring-boot
的META-INF/spring.factories
文件,包含以下内容::
1 | # Application Listeners |
这些监听器会贯穿springboot整个生命周期。至此,对SpringApplication实例的初始化过程就结束了。
run 方法
主要分为八个步骤,注释在代码中了
1 | public ConfigurableApplicationContext run(String... args) { |
第一步:获取并启动监听器
跟进方法,这里流程和获取初始化器、监听器一样,传入的 class 为SpringApplicationRunListener.class
1 | private SpringApplicationRunListeners getRunListeners(String[] args) { |
查看META-INF/spring.factories
文件相应的key-value,只有一个类。
1 | # Run Listeners |
查看这个类的构造方法
1 | public EventPublishingRunListener(SpringApplication application, String[] args) { |
我们在实例化EventPublishingRunListener的时候,是会将当前SpringApplication实例传入的,这个实例中含有我们一开始所有生成的监听器实例。构造器中new了一个SimpleApplicationEventMulticaster
广播器实例,并将application中的listener添加到广播器中,这里使用了一个内部类来存储所有监听器。
现在知道EventPublishingRunListener中有一个广播器SimpleApplicationEventMulticaster,这个广播器中又存放所有spring.factories中的监听器
发送开始启动事件
上一步我们通过 getRunListeners
获取的监听器为EventPublishingRunListener
,从名字可以看出是启动事件发布监听器,主要用来发布启动事件。
先看一下这个类所实现的接口,这个接口定义了springboot启动初始化过程中的状态。我们也可以添加自己的监听器,能够监听springboot启动时的各种状态,然后处理自己的逻辑。
1 | public interface SpringApplicationRunListener { |
我们看一下第一个接口,广播器发送了一个ApplicationStartingEvent
事件,所有的事件都继承了SpringApplicationEvent
抽象类
1 |
|
跟进方法
1 |
|
如何调用监听器呢,其实就是执行监听器接口的唯一一个方法onApplicationEvent(event)
1 | private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { |
第二步:准备环境
1 | private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, |
跟进方法,先会判断是否有环境,如归没有的话就根据 webApplicationType 创建一个,如果是web服务的话,就会 new StandardServletEnvironment()
。注意:这里创建的ConfigurableEnvironment环境对象将一直被后面的流程使用
1 | private ConfigurableEnvironment getOrCreateEnvironment() { |
配置完环境后,发送环境已准备的事件,接下来我们看下**ConfigFileApplicationListener
**类,这个监听器非常核心,项目的yml文件都是由其内部类加载的。
1 |
|
继续跟进,会调用onApplicationEnvironmentPreparedEvent
方法来处理ApplicationEnvironmentPreparedEvent
事件。
1 | private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { |
首先loadPostProcessors()
还是会读取spring.factories
文件下对应的类名,将这些环境后处理器
添加到列表并执行后,ConfigFileApplicationListener
会执行它自己的postProcessEnvironment()
方法逻辑。
1 | # Environment Post Processors |
不停地跟踪方法,发现最后 new 了一个Loader
对象并调用其load()
方法。
1 |
|
查看这个内部类
1 | // 构造器 |
构造方法里初始了属性构造器,加载了PropertySourceLoader
类,查看spring.factories
1 | # PropertySource Loaders |
有两个类,分别看一下
1 | // 可以发现 这个类是用来处理 properties 文件的 |
1 | // 这个类是用来加载 yml 和 yaml文件的,我们当然是常用这个了 |
我们继续看load的过程
1 | private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/"; |
当prepareEnvironment
执行完时,项目的环境变量就全部配置完了
这里一共加载了7个配置文件,取值顺序从上往下,也就是上面的会覆盖下面的同名属性。
第三步:创建容器
1 | context = createApplicationContext(); |
跟进方法
1 | // 这个就是web服务的context类 |
第四步:spring容器前置处理
1 | prepareContext(context, environment, listeners, applicationArguments, printedBanner); |
这一步是刷新容器前的准备工作
1 | private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, |
调用初始化器,在这里总算使用了前面设置的初始化器,依次进行调用。这里我们也可以自定义一些初始化器,并将其信息放入spring.factories
文件对应的key中。这是我们项目初始化的一种方式。
1 | protected void applyInitializers(ConfigurableApplicationContext context) { |
加载启动指定类(重点)
之前的代码提到过,在实例化SpringApplication
实例的时候,会将HelloWorldApplication.class
存储到 this.primarySources
的属性中,现在是使用的时候了。
1 | public Set<Object> getAllSources() { |
获取了启动类之后,将它作为参数,调用load方法
1 | protected void load(ApplicationContext context, Object[] sources) { |
我们继续跟进,最终调用
1 | private int load(Class<?> source) { |
启动类HelloWorldApplication.class
被加载到 beanDefinitionMap中,后续该启动类将作为开启自动化配置的入口,后面文章会继续分析,启动类是如何加载,以及自动化配置开启的详细流程。
再之后就是通知相关监听器容器已加载完。
listeners.contextLoaded(context);
第五步:刷新容器
执行到这里,springboot相关的处理工作已经结束,接下的工作就交给spring。
这一步将再之后的文章中详解
第六步:spring容器后置处理
扩展接口,设计模式中的模板方法,默认为空,如果有自定义需求,可以重写该方法,比如打印一些日志。
1 | protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { |
第七步:通知启动结束
1 | listeners.started(context); |
这里和之前的通知有一些不一样
1 | public void starting() { |
第八步: 执行Runners
平常,如果我们想在spring启动完成时执行一些任务都会去实现CommandLineRunner
接口中的run()
方法。
1 | private void callRunners(ApplicationContext context, ApplicationArguments args) { |
callRunner
方法进行了重载,由此可见这两种runner只是入参不同罢了
1 | private void callRunner(ApplicationRunner runner, ApplicationArguments args) { |
至此,spring的启动流程大概就分析结束了,很有一些细节和核心部分将在之后的文章继续讲解。