
玩转SpringBoot—启动源码及外部化配置
作者 | 宇木木兮
来源 |今日头条
学习目标
理解springboot的总体启动流程,并能口述大概
理清配置文件的加载流程
第1章 main入口
上面代码主要做了两件事情。
- 第一步new了一个SpringApplication对象
- 第二步调用了run()方法。
1.1 SpringApplication
1.1.1 判断运行环境
构造方法内会调用枚举WebApplicationType的deduceFromClasspath方法获得应用类型并设置当前应用是普通web应用、响应式web应用还是非web应用。
deduceFromClasspath方法由枚举WebApplicationType提供,具体实现如下:
推断的过程中重点调用了ClassUtils.isPresent()方法,用来判断指定类名的类是否存在,是否可以进行加载。ClassUtils.isPresent()方法源代码如下:
isPresent()方法调用了forName()方法,如果在调用forName()方法的过程中出现异常则返回false,也就是目标类不存在。否则,返回true。
看一下forName()方法的部分代码:
通过以上核心代码,可得知forName()方法主要做的事情就是获得类加载器,尝试直接加载类,如果失败则尝试加载该类的内部类,如果依旧失败,则抛出异常。
因此,整个应用类型的推断分以下步骤:
- SpringBoot调用SpringApplication构造方法;
- SpringApplication构造方法调用枚举类的类型推断方法deduceFromClasspath()。
- deduceFromClasspath()方法通过ClassUtils.isPresent()返回结果为true或false来确定是否加载成功指定的类。
- ClassUtils.isPresent()方法通过调用forName()方法并捕获异常来确定是否能够成功加载该类。
- forName()方法通过尝试加载指定类和指定类的内部类来确定该类是否存在,存在则返回该类,不存在则抛异常。
在类型推断的过程中枚举类WebApplicationType定义了具体去加载哪些类:
- 如果应用程序存在DispatcherHandler并且不存在DispatcherServlet和ServletContainer则为响应式web应用,需加载并启动内嵌的响应式web服务。
- 如果应用程序不包含Servlet和ConfigurableWebApplicationContext则为普通应用程序。
- 其他情况则为基于servlet的web应用,需加载并启动内嵌的web服务。
1.1.2 初始化器和监听器
利用SPI机制扫描 META-INF/spring.factories 这个文件,并且加载
ApplicationContextInitializer、ApplicationListener 接口实例。
1、ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用
2、ApplicationListener 当springboot启动时事件change后都会触发
总结:上面就是SpringApplication初始化的代码,new SpringApplication()没做啥事情 ,利用SPI机制主要加载了META-INF/spring.factories 下面定义的事件监听器接口实现类
1.2 执行run方法
第2章 环境变量及配置
2.1 prepareEnvironment
2.2 getOrCreateEnvironment
枚举WebApplicationType中定义了三个应用类型:
- NONE:应用程序不作为web应用启动,不启动内嵌的服务。
- SERVLET:应用程序以基于servlet的web应用启动,需启动内嵌servlet web服务。
- REACTIVE:应用程序以响应式web应用启动,需启动内嵌的响应式web服务。
这里调用new StandardServletEnvironment()方法;
StandardServletEnvironment继承了StandardEnvironment方法,StandardEnvironment又继承了AbstractEnvironment方法;在AbstractEnvironment方法中调用了customizePropertySources方法
customizePropertySources方法会回调StandardServletEnvironment方法中的customizePropertySources方法,
到这里为止propertySources里面就加载了servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment
然后回到prepareEnvironment方法中,在
listeners.environmentPrepared(bootstrapContext, environment);方法中去进行监听
2.3 environmentPrepared
继续进入environmentPrepared方法,会进入到SpringApplicationRunListener接口,这个接口在run方法中的getRunListeners里面获取,最终是在sprin.factories里面进行加载实现类EventPublishingRunListener,执行的是EventPublishingRunListener类中的environmentPrepared方法。
multicastEvent
invokeListener
doInvokeListener
进入ConfigFileApplicationListener实现类中的onApplicationEvent方法
onApplicationEvent
4.2.4 createApplicationContext
一起来看下context = createApplicationContext(); 这段代码,这段代码主要是根据项目类型创建上下文,并且会注入几个核心组件类。
Web类型项目创建上下文对象AnnotationConfigServletWebServerApplicationContext 。这里会把 ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等一些核心组件加入到Spring容器
2.5 refreshContext
下面一起来看下refreshContext(context) 这个方法,这个方法启动spring的代码加载了bean,还启动了内置web容器
转到AbstractApplicationContext - >refresh()方法里面发现这是spring容器启动代码
