为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot(五)启动流程分析

标签:
SpringBoot

学习过springboot的都知道,在Springboot的main入口函数中调用SpringApplication.run(DemoApplication.class,args)函数便可以启用SpringBoot应用程序,跟踪一下SpringApplication源码可以发现,最终还是调用了SpringApplication的动态run函数。

下面以SpringBoot2.0.3.RELEASE为例简单分析一下运行过程。

SpringApplicatiton部分源码:

1 public static ConfigurableApplicationContext run(Class<?>[] primarySources,2       String[] args) {3   //创建springapplication对象,调用函数run(args)4    return new SpringApplication(primarySources).run(args);5 }

上面的源码可以发现还是先创建SpringApplication实例,再调用run方法

第一步 分析 SpringApplication构造函数

SpringApplication构造函数代码如下:

复制代码

 1  public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { 2    this.resourceLoader = resourceLoader; 3    Assert.notNull(primarySources, "PrimarySources must not be null"); 4    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); 5  6   //1:判断web环境 7    this.webApplicationType = deduceWebApplicationType(); 8  9   //2:加载classpath下META-INF/spring.factories中配置的ApplicationContextInitializer10    setInitializers((Collection) getSpringFactoriesInstances(11          ApplicationContextInitializer.class));12   //3:加载classpath下META-INF/spring.factories中配置的ApplicationListener13   14    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));15   //4:推断main方法所在的类16    this.mainApplicationClass = deduceMainApplicationClass();17 }

复制代码

具体逻辑分析:

  1. deduceWebApplicationType(),  SpringApplication构造函数中首先初始化应用类型,根据加载相关类路径判断应用类型,具体逻辑如下:

复制代码

 1   private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework." 2       + "web.reactive.DispatcherHandler"; 3  4   private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework." 5       + "web.servlet.DispatcherServlet"; 6  7   private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", 8       "org.springframework.web.context.ConfigurableWebApplicationContext" }; 9 10 11 12    private WebApplicationType deduceWebApplicationType() {13   //当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时14    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)15          && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {16       return WebApplicationType.REACTIVE;17    }18   //当加载的类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时,返回标准应用19    for (String className : WEB_ENVIRONMENT_CLASSES) {20       if (!ClassUtils.isPresent(className, null)) {22          return WebApplicationType.NONE;23       }24    }25   //加载的类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型则判断为web应用26    return WebApplicationType.SERVLET;27 }

复制代码

  2. setInitializers初始化属性initializers,加载classpath下META-INF/spring.factories中配置的ApplicationContextInitializer,此处getSpringFactoriesInstances方法入参type=ApplicationContextInitializer.class

复制代码

 1   private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, 2         Class<?>[] parameterTypes, Object... args) { 3      ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 4      // Use names and ensure unique to protect against duplicates 5     // SpringFactoriesLoader.loadFactoryNames()方法将会从calssptah下的META-INF/spring.factories中读取key为//org.springframework.context.ApplicationContextInitializer的值,并以集合形式返回 6      Set<String> names = new LinkedHashSet<>( 7            SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 8      //根据返回names集合逐个实例化,也就是初始化各种ApplicationContextInitializer,这些Initializer实际是在Spring上下文ApplicationContext执行refresh前调用 9      List<T> instances = createSpringFactoriesInstances(type, parameterTypes,10          classLoader, args, names);11      AnnotationAwareOrderComparator.sort(instances);12      return instances;13   }

复制代码

       3. setListeners 初始化属性listeners,加载classpath下META-INF/spring.factories中配置的ApplicationListener,此处入参为getSpringFactoriesInstances方法入参type= ApplicationListener.class

复制代码

 1    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, 2         Class<?>[] parameterTypes, Object... args) { 3      ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 4      // Use names and ensure unique to protect against duplicates 5     // SpringFactoriesLoader.loadFactoryNames()方法将会从calssptah下的META-INF/spring.factories中读取key为//org.springframework.context.ApplicationListener的值,并以集合形式返回 6      Set<String> names = new LinkedHashSet<>( 7          SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 8     //根据配置,初始化各种ApplicationListener,作用是用来监听ApplicationEvent 9      List<T> instances = createSpringFactoriesInstances(type, parameterTypes,10          classLoader, args, names);11      AnnotationAwareOrderComparator.sort(instances);12      return instances;13   }

复制代码

第二步 分析 SpringApplication中 run方法

SpringApplication的run方法代码如下:

复制代码

 1 public ConfigurableApplicationContext run(String... args) { 2    StopWatch stopWatch = new StopWatch(); 3    stopWatch.start(); 4    ConfigurableApplicationContext context = null; 5    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 6     //设置系统变量java.awt.headless 7    configureHeadlessProperty(); 8     //1:加载classpath下面的META-INF/spring.factories SpringApplicationRunListener 9    SpringApplicationRunListeners listeners = getRunListeners(args);10     //2:执行所有runlistener的starting方法,实际上发布一个【ApplicationStartingEvent】事件11    listeners.starting();12    try {13       //3:实例化ApplicationArguments对象14       ApplicationArguments applicationArguments = new DefaultApplicationArguments(15             args);16       //4: 创建Environment (web环境 or 标准环境)+配置Environment,主要是把run方法的参数配置到Environment  发布【ApplicationEnvironmentPreparedEvent】事件17       ConfigurableEnvironment environment = prepareEnvironment(listeners,18             applicationArguments);19           configureIgnoreBeanInfo(environment);20     //打印banner,SpringBoot启动时,控制台输出的一个歪歪扭扭的很不清楚的Spring几个大字母,也可以自定义,参考博客:http://majunwei.com/view/201708171646079868.html21       Banner printedBanner = printBanner(environment);22     //5: 根据不同environment实例化context23       context = createApplicationContext();24     // 异常处理25       exceptionReporters = getSpringFactoriesInstances(26             SpringBootExceptionReporter.class,27             new Class[] { ConfigurableApplicationContext.class }, context);28     //6: 上下文相关预处理                  发布【ApplicationPreparedEvent】事件29       prepareContext(context, environment, listeners, applicationArguments,30             printedBanner);31     //7: 执行context的refresh,并且调用context的registerShutdownHook方法32       refreshContext(context);33     //8:空方法34       afterRefresh(context, applicationArguments);35       stopWatch.stop();36       if (this.logStartupInfo) {37          new StartupInfoLogger(this.mainApplicationClass)38                .logStarted(getApplicationLog(), stopWatch);39       }40      //9:执行所有runlisteners的started方法,发布【ApplicationStartedEvent】事件41       listeners.started(context);42      //10: 遍历执行CommandLineRunner和ApplicationRunner43      //如果需要在SpringBoot应用启动后运行一些特殊的逻辑,可以通过实现ApplicationRunner或CommandLineRunner接口中的run方法,该自定义类的run方法会在此处统一调用44       callRunners(context, applicationArguments);45    }46    catch (Throwable ex) {47       handleRunFailure(context, ex, exceptionReporters, listeners);48       throw new IllegalStateException(ex);49    }50 51    try {52       listeners.running(context);53    }54    catch (Throwable ex) {55       handleRunFailure(context, ex, exceptionReporters, null);56       throw new IllegalStateException(ex);57    }58    return context;59 }

复制代码

具体分析:

  1. getRunListeners(args) 加载各种SpringApplicationRunListener实例,内部实现也还是通过SpringFactoriesLoader.loadFactoryNames(type, classLoader))实现,加载META-INF/spring.factories中key为org.springframework.boot.SpringApplicationRunListener的值,生成对应实例。

  2.  listeners.starting()  执行所有SpringApplicationRunListener的stating方法,发布ApplicationStartedEvent事件,该事件被ApplicationListener类型的listener监听

  3.  实例化ApplicationArguments对象

  4 . 配置环境并发布ApplicationEnvironmentPreparedEvent事件

复制代码

 1    private ConfigurableEnvironment prepareEnvironment( 2       SpringApplicationRunListeners listeners, 3       ApplicationArguments applicationArguments) { 4      // Create and configure the environment 5    ConfigurableEnvironment environment = getOrCreateEnvironment(); 6   //configureEnvironment配置properties和profiles 7    configureEnvironment(environment, applicationArguments.getSourceArgs()); 8   // 执行EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件,将会被ApplicationListener监听到 9    listeners.environmentPrepared(environment);10   //11    bindToSpringApplication(environment);12    if (this.webApplicationType == WebApplicationType.NONE) {13       environment = new EnvironmentConverter(getClassLoader())14             .convertToStandardEnvironmentIfNecessary(environment);15    }16    ConfigurationPropertySources.attach(environment);17    return environment;18 }

复制代码

备注:实际上载spring-boot-2.0.3.RELEASE.jar包中,可以发现spring.factories中只配置了一个RunListener: org.springframework.boot.context.event.EventPublishingRunListener

     截取EventPublishingRunListener.java部分代码:

复制代码

 1 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { 2      3  4      public EventPublishingRunListener(SpringApplication application, String[] args) { 5        this.application = application; 6        this.args = args; 7        this.initialMulticaster = new SimpleApplicationEventMulticaster(); 8       //将SpringApplication实例中的ApplicationListener类型的listeners添加到initialMulticaster,后续执行监听 9      for (ApplicationListener<?> listener : application.getListeners()) {10         this.initialMulticaster.addApplicationListener(listener);11      }12   }13 14   // 发布一个ApplicationEnvironmentPreparedEvent事件15     @Override16     public void environmentPrepared(ConfigurableEnvironment environment) {17         //所有被添加到initialMulticaster中的listener都将监听ApplicationEnvironmentPreparedEvent事件18         this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(19                 this.application, this.args, environment));20     }21 22 }

复制代码

  5. 根据environment类型创建ApplicationContext

  6. 上下文相关处理:

复制代码

 1 private void prepareContext(ConfigurableApplicationContext context, 2       ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, 3       ApplicationArguments applicationArguments, Banner printedBanner) { 4    context.setEnvironment(environment); 5   //配置beanNameGenerator和资源加载器 6    postProcessApplicationContext(context); 7   //回调所有的ApplicationContextInitializer 8    applyInitializers(context); 9   //执行所有SpringApplicationRunListener的contextPrepared方法,触发事件,实际上EventPublishingRunListener中contextPrepared是一个空方法,什么都没执行10    listeners.contextPrepared(context);11    if (this.logStartupInfo) {12       logStartupInfo(context.getParent() == null);13       logStartupProfileInfo(context);14    }15 16   //向Spring容器注入springApplicationArguments和springBootBanner17    // Add boot specific singleton beans18    context.getBeanFactory().registerSingleton("springApplicationArguments",19          applicationArguments);20    if (printedBanner != null) {21       context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);22    }23 24    // Load the sources25    Set<Object> sources = getAllSources();26    Assert.notEmpty(sources, "Sources must not be empty");27    load(context, sources.toArray(new Object[0]));28   //执行所有SpringApplicationRunListener的contextLoaded方法,下面是EventPublishingRunListener中的contextLoaded29    listeners.contextLoaded(context);30 }

复制代码

EventPublishingRunListener.java中contextLoaded方法具体实现

复制代码

 1 public void contextLoaded(ConfigurableApplicationContext context) { 2    for (ApplicationListener<?> listener : this.application.getListeners()) { 3       if (listener instanceof ApplicationContextAware) { 4          ((ApplicationContextAware) listener).setApplicationContext(context); 5       } 6       context.addApplicationListener(listener); 7    } 8   //触发ApplicationPreparedEvent事件,ApplicationListener负责监听 9    this.initialMulticaster.multicastEvent(10          new ApplicationPreparedEvent(this.application, this.args, context));11 }

复制代码

  7.  执行context的refresh,并且调用context的registerShutdownHook方法

  8. afterRefresh空方法

  9. 执行所有runlisteners的started方法,发布ApplicationStartedEvent事件

  10. 遍历执行CommandLineRunner和ApplicationRunner

以上。

原文出处:https://www.cnblogs.com/ashleyboy/p/9563565.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消