Spring Boot(五)启动流程分析

学习过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中配置的ApplicationContextInitializer 10 setInitializers((Collection) getSpringFactoriesInstances( 11 ApplicationContextInitializer.class)); 12   //3:加载classpath下META-INF/spring.factories中配置的ApplicationListener 13    14 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 15   //4:推断main方法所在的类 16 this.mainApplicationClass = deduceMainApplicationClass(); 17 } 复制代码 具体逻辑分析: 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 Collection getSpringFactoriesInstances(Class 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 names = new LinkedHashSet<>( 7   SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 8   //根据返回names集合逐个实例化,也就是初始化各种ApplicationContextInitializer,这些Initializer实际是在Spring上下文ApplicationContext执行refresh前调用 9    List 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 Collection getSpringFactoriesInstances(Class 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 names = new LinkedHashSet<>( 7 SpringFactoriesLoader.loadFactoryNames(type, classLoader)); 8     //根据配置,初始化各种ApplicationListener,作用是用来监听ApplicationEvent 9   List 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 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.html 21 Banner printedBanner = printBanner(environment); 22     //5: 根据不同environment实例化context 23 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和ApplicationRunner 43  //如果需要在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 @Override 16 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和springBootBanner 17 // Add boot specific singleton beans 18 context.getBeanFactory().registerSingleton("springApplicationArguments", 19 applicationArguments); 20 if (printedBanner != null) { 21 context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); 22 } 23 24 // Load the sources 25 Set sources = getAllSources(); 26 Assert.notEmpty(sources, "Sources must not be empty"); 27 load(context, sources.toArray(new Object[0])); 28   //执行所有SpringApplicationRunListener的contextLoaded方法,下面是EventPublishingRunListener中的contextLoaded 29 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和ApplicationRunnerhttps://www.cnblogs.com/ashleyboy/p/9563565.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信