Springboot浅析(二)——容器启动流程

大概是水平有限,最近跟读代码与相关书籍感受巨费时间,想深刻弄明白全部的东西很难,因此也只能带着问题来学习springboot了,之后遇到确切的问题再作深刻了解把,给本身定个目标,暂时只弄清楚容器启动大致流程,了解组件扫描,自动配置,解决循环依赖这几个问题。 通常启动的Main方法为SpringApplication.run(启动类.class, args);,跟下去的话会发现调用的就是new SpringApplication(启动类).run(args)因为容器刷新内容最关键也最复杂,先来了解下除容器刷新以外的流程。java

(一) SpringApplication的初始化

1.代码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null"); 
  //一般状况下primarySources就是启动类,暂时理解这里就是将启动类设置为主配置资源来源
 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 
 //经过类路径中寻找相关类,判断当前环境是NONE(标准环境(classPath下没有javax.servlet.Servlet以及org.springframework.web.context.ConfigurableWebApplicationContext)、SERVLET(Servlet环境)、REACTIVE(响应式)
 this.webApplicationType = WebApplicationType.deduceFromClasspath();
//添加initializers,设置初始化器,这些初始化器将在在容器刷新前回调,原理是经过SpringFactoriesLoader的loadFactoryNames方法在 spring.factories文件中找到的ApplicationContextInitializer接口的配置的实现类的全限定类名,并实例化。
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 //同上,设置ApplicationListener,添加 spring.factories文件中ApplicationListener配置的响应实现类。
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 
  //经过构造一个运行时异常,而后去栈帧中寻找方法名为main的方法来获得入口类的名字并设置为mainApplicationClass
 this.mainApplicationClass = deduceMainApplicationClass(); 
}

2.注

(1)ApplicationContextInitializer有哪些?

debug发现有这些:web

他们的做用是:spring

  • ConfigurationWarningsApplicationContextInitializer:报告IOC容器的一些常见的错误配置
  • ContextIdApplicationContextInitializer:设置Spring应用上下文的ID
  • DelegatingApplicationContextInitializer:加载 application.properties 中 context.initializer.classes 配置的类
  • ServerPortInfoApplicationContextInitializer:将内置servlet容器实际使用的监听端口写入到 Environment 环境属性中
  • SharedMetadataReaderFactoryContextInitializer:建立一个 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory 对象
  • ConditionEvaluationReportLoggingListener:将 ConditionEvaluationReport 写入日志

(2)ApplicationListener有哪些

debug发现有这些:编程

他们的做用是:缓存

  • ClearCachesApplicationListener:应用上下文加载完成后对缓存作清除工做
  • ParentContextCloserApplicationListener:监听双亲应用上下文的关闭事件并往本身的子应用上下文中传播
  • FileEncodingApplicationListener:检测系统文件编码与应用环境编码是否一致,若是系统文件编码和应用环境的编码不一样则终止应用启动
  • AnsiOutputApplicationListener:根据 spring.output.ansi.enabled 参数配置 AnsiOutput
  • ConfigFileApplicationListener:从常见的那些约定的位置读取配置文件
  • DelegatingApplicationListener:监听到事件后转发给 application.properties 中配置的 context.listener.classes 的监听器
  • ClasspathLoggingApplicationListener:对环境就绪事件 ApplicationEnvironmentPreparedEvent 和应用失败事件 ApplicationFailedEvent 作出响应
  • LoggingApplicationListener:配置 LoggingSystem。使用 logging.config 环境变量指定的配置或者缺省配置
  • LiquibaseServiceLocatorApplicationListener:使用一个能够和 SpringBoot 可执行jar包配合工做的版本替换 LiquibaseServiceLocator
  • BackgroundPreinitializer:使用一个后台线程尽早触发一些耗时的初始化任务

(3)REACTIVE是什么

REACTIVE是响应式编程的东西,指的是应用WebFlux框架下的应用环境,是NIO同步非阻塞IO,将来可能替代当前的MVC,因为是比较新的技术,应用场景比较有限,暂时不作深刻了解。springboot

(二)容器刷新以前的操做

1.代码

public ConfigurableApplicationContext run(String... args) {  
//这个组件是用来监控启动时间的,不是很重要
StopWatch stopWatch = new StopWatch();  
stopWatch.start();  
ConfigurableApplicationContext context = null;  
//SpringBootExceptionReporter这个东西是一个异常解析器,实现类只有一个是FailureAnalyzers,
//用于打印异常信息,这个集合在下面③处会初始化,集合里面装了针对各式各样的解析器,
//在catch到异常后,会遍历这个集合,寻找合适的解析器,而后打印异常日志
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 
//刷新系统属性java.awt.headless的值,若是没有值则设为true,这个值表示无头模式(意指缺乏显示设备,键盘或鼠标的系统配置),
//在无头模式下java.awt.Toolkit将使用特定的无头模式下的实现类,由于就算没有显示设备,有些操做任可以被容许。
configureHeadlessProperty();  
//①
SpringApplicationRunListeners listeners = getRunListeners(args);  
listeners.starting();  
try {  
//②
  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  
  ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  //配置系统参数spring.beaninfo.ignore,默认值为ture,字面意思是跳过搜索BeanInfo类,但具体是什么我暂时也不清楚。
  configureIgnoreBeanInfo(environment);
  //③
  Banner printedBanner = printBanner(environment);  
  //④
  context = createApplicationContext();  
  //⑤
  exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,  
 new Class[]{ConfigurableApplicationContext.class}, context);  
 //⑥
  prepareContext(context, environment, listeners, applicationArguments, printedBanner);  
  refreshContext(context);  
  //刷新后的处理,是个空实现
  afterRefresh(context, applicationArguments); 
  //计时器结束
  stopWatch.stop();  
 if (this.logStartupInfo) {  
  new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);  
  }  
  //发布started事件
  listeners.started(context);  
  //运行器回调,即实现了ApplicationRunner接口或CommandLineRunner接口的bean
  callRunners(context, applicationArguments);  
} catch (Throwable ex) {  
  handleRunFailure(context, ex, exceptionReporters, listeners);  
 throw new IllegalStateException(ex);  
}  
  
try {  
  listeners.running(context);  
} catch (Throwable ex) {  
  handleRunFailure(context, ex, exceptionReporters, null);  
 throw new IllegalStateException(ex);  
}  
return context;
}

2.代码注释

内部又是调getSpringFactoriesInstances方法,取spring.factories中全部的SpringApplicationRunListener,而后对外暴露SpringApplicationRunListeners。 SpringApplicationRunListeners封装全部的SpringApplicationRunListener,用于容器启动间的事件发布到全部的SpringApplicationRunListener中。app

SpringApplicationRunListener中定义的方法有:框架

  • void starting();首次启动run方法时当即调用。可用于很是早期的初始化。
  • void environmentPrepared(ConfigurableEnvironment environment);准备好环境(Environment构建完成),但在建立ApplicationContext以前调用。
  • void contextPrepared(ConfigurableApplicationContext context);在建立和构建ApplicationContext以后,但在加载以前调用。
  • void contextLoaded(ConfigurableApplicationContext context);ApplicationContext已加载但在刷新以前调用。
  • void started(ConfigurableApplicationContext context);ApplicationContext已刷新,应用程序已启动,但还没有调用CommandLineRunners和ApplicationRunners
  • void running(ConfigurableApplicationContext context);在运行方法完全完成以前当即调用,刷新ApplicationContext并调用全部CommandLineRunners和ApplicationRunner。
  • void failed(ConfigurableApplicationContext context, Throwable exception);在运行应用程序时失败时调用。

值得注意的是,started、running、failed方法是 SpringBoot2.0 才加入的。less

经过Debug,发现默认状况下加载的listeners有一个,类型为 EventPublishingRunListener。它在SpringBoot应用启动的不一样时间点发布不一样应用事件类型(ApplicationEvent),若是有哪些事件监听者(ApplicationListener)对这些事件感兴趣,则能够接收而且处理。SpringApplicationRunListener与ApplicationListener的区别是SpringApplicationRunListener比ApplicationListener更靠前,SpringApplicationRunListener监听的是SpringApplication相关方法的执行,属于第一层监听器,他会发布相应的事件给ApplicationListener。学习

根据不一样的webApplicationType完成Environment的初始化,通常是使用StandardServletEnvironment实现类,Environment用于描述应用程序当前的运行环境,其抽象了两个方面的内容:配置文件(profile)和属性(properties),其实就是对应的配置文件、环境变量、命令行参数里面的内容。这里Environment构建完成时发布了environmentPrepared事件,而且将最新的配置值绑定到了SpringbootApplication中,也就是当前的对象中。好比yml里面配的spring. main开头的一些属性值。

根据Enviroment中配置获取对应的banners没有则用默认的SpringbootBanner打印启动信息,就是启动应用时候控制台打印的logo

根据WebApplicationType,反射建立不一样的ApplicationContext实现(Servlet是AnnotationConfigServletWebServerApplicationContext)。这里Servlet是AnnotationConfigServletWebServerApplicationContext,在他的父类GenericApplicationContext构造方法中,其中注入了一个DefaultListableBeanFactory,这个BeanFactory很关键,实际上AnnotationConfigServletWebServerApplicationContext的BeanFactory能力就是从DefaultListableBeanFactory扩展而来。 另外在这一步中也注册了ConfigurationClassPostProcessor、DefaultEventListenerFactory、EventListenerMethodProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor这些beanDefinition,做为基础组件。ConfigurationClassPostProcessor这个组件是最重要的,其余的暂时没有深究什么做用,ConfigurationClassPostProcessor是BeanFactoryPostProcessor,负责在容器刷新时加载扫描配置类注解进行组件解析,注册BeanDefinition。

建立一系列SpringBootExceptionReporter,建立流程是经过SpringFactoriesLoader获取到全部实现SpringBootExceptionReporter接口的class,

初始化ApplicationContext,主要完成如下工做:

  • 将准备好的Environment设置给ApplicationContext
  • 进一步执行ApplicationContext的后置处理,包括注册BeanName生成器, 设置资源加载器和类加载器,设置类型转换器ConversionService等等,这里的东西暂时不用深究。
  • 遍历调用全部的ApplicationContextInitializer的 initialize()方法来对已经建立好的ApplicationContext进行进一步的处理。
  • 调用SpringApplicationRunListener的 contextPrepared()方法,通知全部的监听者:ApplicationContext已经准备完毕。
  • 建立启动类的beanDefiniton注册到容器中。
  • 调用SpringApplicationRunListener的 contextLoaded()方法,通知全部的监听者:ApplicationContext已经装载完毕。

小结

容器刷新前,整个流程分三个步骤:

  1. 初始化SpringApplication对象,好比设置webApplicationType,加载ApplicationListener,ApplicationContextInitializer。
  2. 初始化Environment对象,封装配置文件,命令行参数。
  3. 初始化ConfigurableApplicationContext(与ApplicationContext的区别是ConfigurableApplicationContext能够对容器进行写,而ApplicationContext只提供读的方法),而且将启动类的beanDefiniton先行注册到容器中。 这是主线程能够看到的,其余没有看到的ApplicationContextInitializer与ApplicationListener干了什么暂时尚未进行深究。
相关文章
相关标签/搜索