目录
纵览springboot启动过程会发现,其本质是创建1个IOC容器,也就是存放bean的容器,使用自动装配简化了spring的各种bean配置(xml文件),启动的所有过程都是为了构建这个bean容器服务。本来首先分析启动类上的注解,再分析SpringApplication的构建过程和run方法流程。
SpringBootApplication注解
启动类上的@SpringBootApplication注解是组合注解:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
由三个注解组成:
@SpringBootConfiguration: 本质上是@Configuration注解,@Configuration等价于<Beans></Beans>,其中如果有@Bean注解其等价于<Bean></Bean>,这里加@Configuration注解表明这个启动类就是个配置类;
@EnableAutoConfiguration :扫描spring.factories文件,自动配置;
@ComponentScan: 告诉Spring 哪个packages 的用注解标识的类会被spring自动扫描并且装入bean容器,这里会排除spring.factories文件的value;
重点关注的是@EnableAutoConfiguration和@ComponentScan注解
@EnableAutoConfiguration注解
它也是一个组合注解,首先关注@Import(AutoConfigurationImportSelector)这个注解,@Import这个注解类是用于导入配置类可以理解java程序中的Import 包和类;比如我们可以使用@Import(test.class)来把test.class放到bean容器,这里导入AutoConfigurationImportSelector有什么用呢;
AutoConfigurationImportSelector是一个接口,其有一个方法getAutoConfigurationEntry(),
该方法读取spring.factories中的所有配置类列表(也包括依赖包里面的META-INF下的配置文件spring.factories)
最终@EnableAutoConfiguration注解实现的作用就是META-INF/spring.factories配置文件的value进行排序过滤后返回;
@AutoConfigurationPackage注解
@EnableAutoConfiguration还有个@AutoConfigurationPackage注解是做什么用的,其实就是个包路径,也就是作用范围的指定。将添加该注解的类所在的package 作为 自动配置package 进行管理,@Import(AutoConfigurationPackages.Registrar)表示对于使用了该注解的类所在的包,应当使用AutoConfigurationPackages注册。
注册类和方法如下:自动扫描同级及以下的包,如果有包在@AutoConfigurationPackage注解范围以上扫描不到。
@ComponentScan注解
@ComponentScan用于类或接口上主要是指定扫描路径,spring会把指定路径下带有指定注解的类注册到IOC容器中。会被自动装配的注解包括@Controller、@Service、@Component、@Repository等等。@EnableAutoConfiguration注解实现了spring.factories中配置类的自动配置,@ComponentScan实现了用户指定的类的自动注入;如果不设置的话默认扫描@ComponentScan注解所在类的同级类和同级目录下的所有类,所以对于一个Spring Boot项目,一般会把入口类放在顶层目录中,这样就能够保证源码目录下的所有类都能够被扫描到;
SpringBoot启动过程
上面说了springboot的启动流程本质上创建IOC容器,从实现上分为两步:创建SpringApplication对象和调用run方法;从功能上看可以分为4步:构建服务、创建环境、创建容器、填充容器;
构建服务:就是new SpringApplication对象的过程,期间会推断web应用类型,从spring.factories中获取初始化器和监听器;
创建环境:environment创建过程读取很多的资源信息和配置;
创建容器:这里的容器就是Context上下文也是bean工厂;
填充容器:填充容器就是refresh后的过程;
构建服务
上面说了构建服务就是创建 SpringApplication对象,做了以下几个事情:
- 保存一些信息。
- 判定当前应用的类型。ClassUtils。Servlet
- bootstrappers:初始启动引导器(List<Bootstrapper>):去spring.factories文件中找 org.springframework.boot.Bootstrapper
- 找 ApplicationContextInitializer;去spring.factories找 ApplicationContextInitializer,得到List<ApplicationContextInitializer<?>> initializers
- 找 ApplicationListener ;应用监听器。去spring.factories找 ApplicationListener,得到List<ApplicationListener<?>> listeners
创建环境
上一步完成后,就到了run方法了,后面的几个都是run方法后的过程:
- StopWatch,记录应用的启动时间
- 创建引导上下文(Context环境)createBootstrapContext(),获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置
- 让当前应用进入headless模式。java.awt.headless;
- 获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】,getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
- 遍历 SpringApplicationRunListener 调用 starting 方法,相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
- 保存命令行参数;ApplicationArguments
- 准备环境 prepareEnvironment():返回或者创建基础环境信息对象,StandardServletEnvironment,配置环境信息对象,读取所有的配置源的配置属性值,绑定环境信息,监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成;
创建容器
这一步就是要1个创建IOC容器createApplicationContext对象,
- 根据项目类型(Servlet)创建容器,当前会创建AnnotationConfigServletWebServerApplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext():保存环境信息;IOC容器的后置处理流程;应用初始化器applyInitializers;遍历所有的 ApplicationContextInitializer ,调用 initialize来对ioc容器进行初始化扩展功能;遍历所有的 listener调用 contextPrepared。EventPublishRunListenr通知所有的监听器contextPrepared;所有的监听器 调用 contextLoaded;通知所有的监听器contextLoaded;
填充容器
- 刷新IOC容器(refreshContext):创建容器中的所有组件(Spring注解)
- 容器刷新完成后工作(afterRefresh):所有监听器调用 listeners.started(context); 通知所有的监听器 started;调用所有runners的callRunners()获取容器中的 ApplicationRunner;获取容器中的 CommandLineRunner;合并所有runner并且按照@Order进行排序;遍历所有的runner。调用 run 方法;如果以上有异常调用Listener 的 failed;
springboot启动流程:https://www.yuque.com/atguigu/springboot/tmvr0e
springboot启动流程b站:https://www.bilibili.com/video/BV1e14y1A7pT/?vd_source=837a80ccc382967b7ab909f9407a02a4#reply143642551168