前言
我们启动一个springboot项目,最简单的就是配置一个springboot启动类,然后运行即可
通过上面的代码,我们可以看出springboot启动的关键主要有两个地方,
SpringBoot启动流程图
图1:
图2:
一、@SpringBootApplication 注解解析
1.1 @SpringBootApplication
我们直接追踪@SpringBootApplication的源码,可以看到其实@SpringBootApplication是一个组合注解,他分别是由底下这些注解组成。
这些注解虽然看起来很多,但是除去元注解,真正起作用的注解只有以下三个注解:
那这三个注解是有啥用?其实在Spring Boot 1.2版之前,或者我们初学者刚开始接触springboot时,都还没开始使用@SpringBootApplication这个注解,而是使用以上三个注解启动项目。如果有兴趣的,也可以手动敲敲代码,就会发现这样也可以正常启动项目!
所以说这三个注解才是背后的大佬,@SpringBootApplication只是个空壳子。接下来,我来说明下这三个注解各自的作用。
1.2 @SpringBootConfiguration
同样,我们跟踪下@SpringBootConfiguration的源代码,看下他由哪些注解组成
可以看到,除去元注解,剩下的@Configuration注解我相信大家应该都很熟了!我们springboot为什么可以去除xml配置,靠的就是@Configuration这个注解。所以,它的作用就是将当前类申明为配置类,同时还可以使用@bean注解将类以方法的形式实例化到spring容器,而方法名就是实例名,看下代码你就懂了!
作用等同于xml配置文件的
1.3 @ComponentScan
我们先说下@ComponentScan作用。他的作用就是扫描当前包以及子包,将有@Component,@Controller,@Service,@Repository等注解的类注册到容器中,以便调用。 注:大家第一眼见到@ComponentScan这个注解的时候是否有点眼熟?之前,一些传统框架用xml配置文件配置的时候,一般都会使用context:component-scan来扫描包。以下两中写法的效果是相同的`
注:如果@ComponentScan不指定basePackages,那么默认扫描当前包以及其子包,而@SpringBootApplication里的@ComponentScan就是默认扫描,所以我们一般都是把springboot启动类放在最外层,以便扫描所有的类。
1.4 @EnableAutoConfiguration
这里先总结下@EnableAutoConfiguration的工作原理,大家后面看的应该会更清晰: 它主要就是通过内部的方法,扫描classpath的META-INF/spring.factories配置文件(key-value),将其中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项实例化并且注册到spring容器。
ok,我们同样打开@EnableAutoConfiguration源码,可以发现他是由以下几个注解组成的
除去元注解,主要注解就是@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class) 我们springboot项目为什么可以自动载入应用程序所需的bean?就是因为这个神奇的注解@Import。那么这个@Import怎么这么牛皮?没关系!我们一步一步的看下去! 首先我们先进入AutoConfigurationImportSelector类,可以看到他有一个方法selectImports()
继续跟踪,进入getAutoConfigurationEntry()方法,可以看到这里有个List集合,那这个List集合又是干嘛的?没事,我们继续跟踪getCandidateConfigurations()方法!
可以看到这里有个方法,这个方法的作用就是读取classpath下的META-INF/spring.factories文件的配置,将key为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项读取出来,通过反射机制实例化为配置文件,然后注入spring容器。
注:假如你想要实例化一堆bean,可以通过配置文件先将这些bean实例化到容器,等其他项目调用时,在spring.factories中写入这个配置文件的路径即可!
然后直接在SpringFactoriesLoader.loadFactoryNames;这个方法后面打个断点,可以在返回的集合里找到我们自定义的配置文件路径!
说明成功引入我们自定义的依赖包!
二、SpringApplication.run()原理解析
SpringApplication.run()原理相对于前面注解的原理,会稍微麻烦点,为了方便我会适当贴出一些注解代码。 首先我们点击查看run方法的源码
可以看出,其实SpringApplication.run()包括两个部分,一部分就是创建SpringApplicaiton实例,另一部分就是调用run()方法,那他们又是怎么运行的?
2.1 SpringApplicaiton创建
继续跟踪SpringApplication实例的源码
继续跟踪进入,到如下这个方法中
2.1.1 获取应用类型
跟踪deduceFromClasspath方法
从返回结果我们可以看出应用类型一共有三种,分别是
判断一共涉及四个常量: WEBFLUX_INDICATOR_CLASS , WEBMVC_INDICATOR_CLASS,JERSEY_INDICATOR_CLASS,SERVLET_INDICATOR_CLASSES
springboot在初始化容器的时候,会对以上四个常量所对应的class进行判断,看看他们是否存在,从而返回应用类型!至于常量代表哪些class,大家可以自己跟踪看看,也在当前类中
2.1.2 获取初始化器
跟踪进入getSpringFactoriesInstances方法
从代码可以看出是在META-INF/spring.factories配置文件里获取初始化器,然后实例化、排序后再设置到initializers属性中
2.1.3 获取初监听器
其实监听器和初始化的操作是基本一样的
2.1.4 定位main方法
跟踪源码进入deduceMainApplicationClass方法
2.2 SpringApplication.run()调用
2.2.1 Run代码总览
2.2.2 创建spring运行监听器并发布应用启动事件
SpringApplicationRunListener这个接口是干啥的呢?没错,他就是用来加载我们配置文件用的。接下来我弄个简单的例子,大家就知道怎么用了。
2.2.2 番外(listener示例,可跳过)
主要实现自定义监听器并且读取我们配置文件内容,先献上我的文件结构
创建一个maven项目,pom配置只需要添加web依赖即可
在resource自定义配置文件my.properties
自定义监听器,这里主要是对starting、environmentPrepared、started、running方法进行实现
然后编写controller文件对我们的配置参数进行调用
ok,那我们自定义的监听器springboot程序又是如何获取的?前面我们代码里讲过了,它主要是读取META-INF底下的spring.factories文件,然后获取监听器,ok那就简单了,我们直接照着EventPublishingRunListener一样在resource增加METAA_INF/spring.factories文件
最后设置spring启动器
启动!可以看到启动信息
然后我们再调用controller方法

成功读取我们自定义的配置文件,现在再回头看看run方法,是不是就清晰了!
2.2.3 准备基础运行环境
初始化默认应用参数类
根据运行监听器和应用参数来准备spring环境
创建banner的打印类
创建应用的上下文:根据不同哦那个的应用类型初始化不同的上下文应用类
2.2.4 准备上下文
2.2.5 刷新上下文
springboot的启动分为两部分,一部分是注解,一部分是SpringApplication.run(Springboot.class, args),那么我们的注解又是如何嵌入到程序中呢?靠的就是refreshContext方法,同理,我们跟踪源码进入refreshContext方法
到这里,就可以看到一系列bean的操作,继续跟踪进入invokeBeanFactoryPostProcessors(调用在上下文中注册为bean的工厂处理器)方法
进入ConfigurationClassParser这个类后,方法调用也是挺绕的,这里就不深究了…进入这个类主要是想看下它的一些方法,因为对于springboot注解的引用就是在这个类进行的,比如doProcessConfigurationClass:
2.2.6 刷新上下文后置处理
2.2.7 上下文准备后其他处理
停止计时监控类:计时监听器停止,并统计一些任务执行信息
输出日志记录执行主类名、时间信息
发布应用上下文启动完成事件:触发所有SpringapplicationRunListener监听器的started事件方法
执行所有Runner执行器:执行所有applicationRunner和CommandLineRunner两种运行器
发布应用上下文就绪事件:触发所有springapplicationRunnListener的running事件方法
返回上下文
2.3 内置Tomcat
其实,内置tomcat应该要归在refreshContext讲的,因为tomcat就是在注解引入的类中生成的,而refreshContext可以引入注解。不过为了更清楚,这里分开吧。前面说了,我们refreshContext是刷新上下文,那如果想要知道上下文中是否存在生成tomcat的类,我们直接去最后返回的上下文中找对应的类即可!
ok,我们在main方法写获取上下文的代码,并且打印出对应的name
直接启动,可以看到有org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration这个类
ok,我们点开这个类,跟踪源码
我们知道,springboot其实有三种内容服务器,分别是Tomcat,Jetty,Undertow。默认内置tomcat,。我们继续跟踪EmbeddedTomcat.class
可以看到,其实这里的tomcat服务器是内部通过java代码实现的。到这里,run()方法就算结束了。如果后续想深入了解的可以自己看看源码。其实,run()方法总结起来并不多,大方向无非是配置环境参数,引入注解刷新上下文。其他的一些捕获异常、计时操作都是非重点操作
总结
springboot原理:包装spring核心注解,使用springmvc无xml进行启动,通过自定义starter和maven依赖简化开发代码,开发者能够快速整合第三方框架,通过java语言内嵌入tomcat
springboot启动流程
SpringBoot获取factory文件的方式
spring.factory文件中的类的作用
其他
参考:https://blog.csdn.net/weixin_/article/details/ https://www.jianshu.com/p/cb6f923af609

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/138582.html