SpringBoot启动关键点解析 及启动日志追溯

Posted ybkback2018

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot启动关键点解析 及启动日志追溯相关的知识,希望对你有一定的参考价值。

<!doctype html>Spring Boot 源码阅读2

目录

 

 

1 SpringBoot启动关键点解析

 

启动核心分为两部分new SpringApplication(primarySources)和执行run(args)方法,

1.1 new SpringApplication(primarySources)部分

部分的核心是从META/spring.facteries中读取ApplicationContextInitializer和ApplicationListener配置,然后直接通过反射实例化,实例化+排序完毕后,把应用初始化器ApplicationContextInitializer和应用监听放到SpringApplication对象上

1.2 run(args)部分

run部分最核心的构建ConfigurableApplicationContext这个上下文对象对象,构建完毕后当前线程是会返回的。

run方法实现为:

 
 
 
x
 
 
 
 
/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);//获取SpringApplicationRunListener的监听,当前只有org.springframework.boot.context.event.EventPublishingRunListener一个
   listeners.starting(); //调度监听的start方法,这里事件发布监听的start方法是发布了一个ApplicationStartingEvent事件
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
       //创建一个ConfigurableEnvironment(理解参见1.2.1),初始化
       //让configurationProperties的属性源始终位于第一(应该是保证最高优先级)
       //调度应用监听执行environmentPrepared, EventPublishingRunListener一个也就是发布一个ApplicationEnvironmentPreparedEvent事件
       //将获取到的environment中的spring.main配置绑定到SpringApplication的source中(这部分用到了一个Binder语法,没有看懂)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 从环境中读取spring.beaninfo.ignore并设置为系统属性
      configureIgnoreBeanInfo(environment);
        //Banner打印
      Banner printedBanner = printBanner(environment);
        //创建ConfigurableApplicationContext对象,这个上下文对象关注的信息有点多,参见(1.2.2)
      context = createApplicationContext();
       //从META_DATA/spring.facteries中读取SpringBootExceptionReporter的配置并进行实例化,默认读取出来是org.springframework.boot.diagnostics.FailureAnalyzers
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
       //准备环境,参见(1.2.3),主要为设置上下文对象的容器属性
       //调度一开始读取的initializer的执行initialize方法,之后调度监听器的contextPrepared,发布ApplicationContextInitializedEvent
       //打印开始和profile的日志
       //把拿到的标签类进行beanDefinition注册,之后调度监听器的contextLoaded,发布ApplicationPreparedEvent
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
       //刷新上下文,参见(1.2.4),
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
          //打印已启动信息以及耗时情况
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
       //调度监听器的started的方法,此处默认为发布一个ApplicationStartedEvent事件
      listeners.started(context);
       //找注册的ApplicationRunner、CommandLineRunner,排序后分别调度对应的ApplicationRunner和CommandLineRunner (spring-boot的启动后自动执行建表语句好像就是通过这个机制实现。这里可以做一些启动后就需要立马执行的事)。注意,此时刚发布ApplicationStartedEvent而尚未发布ApplicationReadyEvent
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
   try {
       //调度监听器的running方法,此处默认为发布一个ApplicationReadyEvent事件。
       //执行完次应该代表服务正式可用
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}
 

 

1.2.1 ConfigurableEnvironment提供的服务(关注的信息)

从环境接口的方法来看,它主要关注与当前应用的profile、propertySource、systemEnvironment、systemProperties、getConversionService;环境本身重要的信息有webApplicationType(ConversionService是干啥的??)

 

 

 

1.2.2 ConfigurableApplicationContext

从名字解读是可配置的应用容器对象。关注的内容非常多,包括应用监听器(ApplicationListener)、应用的后置处理器(PosProcessor)、应用的协议解析器(ProtocolResolver)、bean管理、类加载器、环境对象管理(Environment)、消息对象管理(Message)、资源对象管理(Resouce)等.

容器对象是根据应用类型有所不同的,SERVLET创建的是org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext的容器对象,该对象在构造时会构造一个AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner对象。

AnnotatedBeanDefinitionReader对象同时设置了beanFactory的dependencyComparator、autowireCandidateResolver实例,进行了ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、EventListenerMethodProcessor、DefaultEventListenerFactory的bean声明。

ClassPathBeanDefinitionScanner对象创建时同时TypeFilter的实例(包含AnnotationTypeFilter(Component.class))

 

 

 

 

 

 

 

 

 

 

 

1.2.3 SpringApplication.prepareContext

 

代码如下:

 
 
 
xxxxxxxxxx
 
 
 
 
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
    //把SpringApplication上的部分信息绑定到context上
   postProcessApplicationContext(context);
    //拿到之前从配置中读取并实例化的initializer,执行initialize(context)方法 !!它们大部分都是给容器设置一些值
   applyInitializers(context);
    //调度监听器的contextPrepared,此处为发布一个ApplicationContextInitializedEvent事件
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
       //打印应用启动的日志
      logStartupInfo(context.getParent() == null);
       //打印profile加载情况
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //把ApplicationArguments注册成单例bean
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
       //如果Banner非空,也注册成单例bean
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
       //设置bean工程的allowBeanDefinitionOverriding属性
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.lazyInitialization) {
       //如果是lazyInitialization模式(默认为false),给上下文追加一个LazyInitializationBeanFactoryPostProcessor的后置处理器实例
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources -》注册的入口类被识别为私有源,需要加入更多的标签类看看是否是在这里就已经被全部识别
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   //把识别出来的源都进行beanDefinition注册
   load(context, sources.toArray(new Object[0]));
   //调度监听器的contextLoaded方法,这里表现为发布一个ApplicationPreparedEvent事件
   listeners.contextLoaded(context);
}
 

 

默认读取并实例化的初始化器有:

0 = {DelegatingApplicationContextInitializer@3271} 1 = {SharedMetadataReaderFactoryContextInitializer@3272} 2 = {ContextIdApplicationContextInitializer@3273} 3 = {ConfigurationWarningsApplicationContextInitializer@3274} 4 = {RSocketPortInfoApplicationContextInitializer@3275} 5 = {ServerPortInfoApplicationContextInitializer@3276} 6 = {ConditionEvaluationReportLoggingListener@3277}

 

1.2.4 SpringApplication.refreshContext

 

 
 
 
 
 
 
 
 
private void refreshContext(ConfigurableApplicationContext context) {
   refresh(context);
   if (this.registerShutdownHook) {
      try {
          //注册关闭虚拟机的钩子
         context.registerShutdownHook(); 
      }
      catch (AccessControlException ex) {
         // Not allowed in some environments.
      }
   }
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
//AbstractApplicationContext.refresh
    
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing. //初始化当前容器的一些属性
        prepareRefresh();
        // Tell the subclass to refresh the internal bean factory. //设置工厂的序列化id
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context. 
        //初始化beanFactory的一些属性,创建一些实例类,也注册了几个beanDefinition
        //设置内容包括 beanClassLoader、StandardBeanExpressionResolver、ResourceEditorRegistrar、ApplicationContextAwareProcessor
        //设置忽略的依赖接口EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
        //注册BeanFactory(SpringApplication)、ResourceLoader(ConfigurableApplicationContext)、ApplicationEventPublisher(ConfigurableApplicationContext)、ApplicationContext(ConfigurableApplicationContext)
        //追加beanPostProcessors:ApplicationListenerDetector
        //管控environment、systemProperties、systemEnvironment单例
        prepareBeanFactory(beanFactory);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            //设置一个WebApplicationContextServletContextAwareProcessor后置处理器
            //设置忽略接口依赖ServletContextAware
            
            //注册并实例化register和session的Scope,在注册过程中通过WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());跳转到sprig-web中
            //注册ServletRequest、ServletResponse、HttpSession、WebRequest的解析以来
            //!!!注意,也就这里引入了对于spring-web的支持 (spring-boot通过spring-boot-starter..等是引入了spring-web的...)
            //这里总结就是注册了spring-web相关的信息
            postProcessBeanFactory(beanFactory);
  

以上是关于SpringBoot启动关键点解析 及启动日志追溯的主要内容,如果未能解决你的问题,请参考以下文章

Java面试题SpringBoot启动原理

SpringBoot启动及自动装配原理

Spring boot 启动过程解析 logback

springboot之jar包Linux后台启动部署及滚动日志查看且日志输出至文件保存(超级详细)

EurekaServer自动装配及启动流程解析

SpringBoot 升级到 2.1 后,启动程序时控制台不打印 API 的解决方法及一些感想