SpringBoot启动流程浅分析
一.启动类
1.@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
关于自动配置的流程,可以去看这篇博文
【Spring Boot】Spring Boot 自动配置原理 图文并茂_秋日的晚霞的博客-CSDN博客
2.main方法
public static void main(String[] args) {
SpringApplication.run(AppTest.class, args);
}
跟进去
首先看方法声明 静态方法 返回一个 ConfigurableApplicationContext 接口实现类对象
可以看到有非常多的实现类及子接口 那到底是怎么选择实现类对象的? 带着这个问题继续往下跟源码
primarySources //主启动类的class对象
args //main方法的启动参数
在这new 了一个 SpringApplication 实例,调用了run方法
new SpringApplication()
跟进 new SpringApplication(primarySources) 方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
创建一个新的 SpringApplication 实例。应用程序上下文将从指定的主要来源加载 bean
继续跟进构造方法
resourceLoader //资源加载器 此时为null
this.resourceLoader = resourceLoader;
primarySources //容器中bean的来源 此时为主启动类的字节码对象
这里断言 primarySources 不能为null 否则将抛出异常
Assert.notNull(primarySources, "PrimarySources must not be null");
方法形参上 primarySources 是一个可变参 接下来将primarySources 变成了一个 LinkedHashSet
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
LinkedHashSet set集合 元素不重复 底层是链表 保证了存储有序
然后执行了 这么一行代码
this.webApplicationType = WebApplicationType.deduceFromClasspath();
这个 webApplicationType 是什么呢 点进去可以看到是一个枚举 有三个枚举项
/**
* The application should not run as a web application and should not start an
* embedded web server.
该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器。
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
应用程序应作为基于 servlet 的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
该应用程序应作为响应式 Web 应用程序运行,并应启动嵌入式响应式 Web 服务器
*/
REACTIVE;
判断当前项目类型 这里返回 WebApplicationType.SERVLET 也就是当前 SpringApplication 的类型为 Web 应用程序,内置servlet Web 服务器
接下来设置了监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
然后设置程序入口
this.mainApplicationClass = deduceMainApplicationClass()
SpringApplication.run()
接下来看run方法做了什么
StopWatch stopWatch = new StopWatch(); //计时器
stopWatch.start(); //开始计时
设置了一个名为java.awt.headless的系统属性
configureHeadlessProperty()
获取Spring应用上下文引导类的监听器,
SpringApplicationRunListeners listeners = getRunListeners(args)
跟进去发现,发布了ApplicationStartingEvent事件
listeners.starting();
封装参数SpringApplication引导类需要的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)
准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
根据当前环境忽略不需要的bean
configureIgnoreBeanInfo(environment);
打印 Banner
Banner printedBanner = printBanner(environment)
创建ApplicationContext上下文 之前看到 webApplicationType 的类型是 SERVLET
context = createApplicationContext()
返回AnnotationConfigServletWebServerApplicationContext字节码对象 然后调用 BeanUtils.instantiateClass 实例化
class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
implements AnnotationConfigRegistry
继承自 ServletWebServerApplicationContext
刷新上下文 最终复用了Spring的刷新容器方法
refreshContext(context);
调用 ConfigurableApplicationContext .refresh()方法
再调用org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#refresh
最后调用 org.springframework.context.support.AbstractApplicationContext#refresh
刷新容器的12个方法可以去看这篇博文
【Spring】SpringIOC容器启动过程源码分析 以及 循环依赖问题_秋日的晚霞的博客-CSDN博客
最后就是停止计时以及打印日志
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}