springboot启动解析一

Springapplication的构造函数方法如下:

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
resourceLoader 我们在实例化SpringApplication设置自己的resourceLoader       
 1.this.resourceLoader = resourceLoader;
        2.Assert.notNull(primarySources, "PrimarySources must not be null");
primarySources 是我们的启动class,同时SpringApplication提供了可以添加的接口
最终都会交给BeanDefinitionLoader,其分别 load这些primarySources 同时还给scanner了,这样scanner避免重复扫描这些类进入容器
        3.this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
检测对应的class是否存在来查看当前springboot的运行环境
        4.this.webApplicationType = deduceWebApplicationType();
寻找"META-INF/spring.factories"文件中的ApplicationContextInitializer类
Initializers是在准备spring容器(只是创建还没有真正刷新spring容器)前调用Initializers的初始化方法
相当于一个扩展点
        5.setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
寻找"META-INF/spring.factories"文件中的ApplicationListener类   
ApplicationListener的onApplicationEvent 是spring用来完成一些操作的时候发送一些事件
让这些listener去做一些操作
    6.setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
mainApplicationClass就是springboot启动的class,为啥不直接用primarySources 里面的,是因为
primarySources 有可能被开发人员修改 所以不准确,mainApplicationClass 主要是做打印日志
等使用
    7.this.mainApplicationClass = deduceMainApplicationClass();
}
其中  deduceMainApplicationClass()方法中有这个
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
其是获取执行到当前方法时候该线程的栈帧中的情况

webApplicationType:是一个枚举类,用来判断当前的springboot项目是什么类型的。

  • 1.None:既不是运行在web 容器下的应用并且也不应该启动一个内置的web服务。
  • 2.SERVLET:是需要运行在基于servlet的web应用且需要启动一个内置servlet-web 服务。
  • 3.REACTIVE:还是运行在 reactive 的web应用中且需要启动一个 reactive-web 服务。

getSpringFactoriesInstances 就是通过系统加载类去指定目录下根据propties文件获取对应的class的全限定名称

ApplicationContextInitializer:是springboot准备调用prepareContext方法准备spring容器(此时spring容器已经创建需要refresh)之前调用的用来在这个时间点执行一些操作:比如设置servletContext等

ApplicationListener:

  • 1.由于spring 有一大堆事件比如ContextRefreshedEvent,ContextStartedEvent等等 我们有一个ApplicationListener 就会监听到这些事件进而做相应的处理。
  • 2.其实都是applicationContext调用其publishEvent 发送一些event,该方法会从很多listener中找到对该event感兴趣的listener 调用其onApplicationEvent,而实现这些的就是SimpleApplicationEventMulticaster,如果有Executor,其会优先使用Executor来执行这就可能导致我们的回调是异步的。
  • 3.applicationContext 还会把event传播到其父容器(我们知道我们常用到AnnotationConfigApplicationContext 已经其有很多父容器)
  • 4.这边还有一个策略就是如果earlyApplicationEvents存在 则先把事件加入到earlyApplicationEvents,稍后在发送,如果不存在则直接发送。
  • 5.applicationevent中包含时间戳,springboot启动类(source),还有我们的spring容器

如果我们想自己在代码中写ApplicationContextInitializer和ApplicationListener的实现类时候可以采用下列方式:

  • 1.在项目中建立一个META-INF/spring.factories文件,里面以key=value 存放我们的实现类
  • 2.手动调用SpringApplication的对应添加方法也可以
  • 3.在我们的application.properties里面配置context.initializer.classes=实现类(通过DelegatingApplicationContextInitializer获取到我们配置的initializer,进而可以保证在prepareContext时候调用),context.listener.classes=实现类(无法监听到springboot启动时候的一些事件,因为那个时候该实现类还未加入容器)
  • 4.虽然我们使用@Configuration来讲我们ApplicationListener的实现类加入到spring容器,且也能监听到程序正常运行的一些事件(无法监听到springboot启动时候的一些事件,因为那个时候该实现类还未加入容器),但是 我们如果想监听全部的事件最好使用上述三种方式配置这样可以在springboot启动的时候就监听

总结SpringApplication的构造方法主要就是设置Initializers和Listeners 同时设置primaryClass方便后面先去加载primaryClass,而且也顺便确定了当前的springboot的运行环境。

你可能感兴趣的:(springboot启动解析一)