0


【SpringBoot源码剥析】| SpringApplication

目录

一. 🦁 概述

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication// ①publicclassMVCApplication{publicstaticvoidmain(String[] args){SpringApplication.run(MVCApplication.class, args);// ②}}
  • 处,使用 @SpringBootApplication 注解,标明是 Spring Boot 应用。通过它,可以开启自动配置的功能。
  • 处,调用 SpringApplication.run(Class<?>... primarySources) 方法,启动 Spring Boot 应用。

上述的代码,是我们使用 Spring Boot 时最常用的代码。而本文,我们先来分析 Spring Boot 应用的启动过程

二. 🦁 SpringApplication

org.springframework.boot.SpringApplication

,Spring 应用启动器。正如其代码上所添加的注释,它来提供启动 Spring 应用的功能。

Class that can be used tobootstrap and launch a Spring application from a Java main method.

大多数情况下,我们都是使用它提供的静态方法:

publicstaticvoidmain(String[] args)throwsException{SpringApplication.run(newClass<?>[0], args);}publicstaticConfigurableApplicationContextrun(Class<?> primarySource,String... args){returnrun(newClass<?>[]{ primarySource }, args);}publicstaticConfigurableApplicationContextrun(Class<?>[] primarySources,String[] args){// 创建 SpringApplication 对象,并执行运行。returnnewSpringApplication(primarySources).run(args);}
  • 前两个静态方法,最终调用的是第 3 个静态方法。而第 3 个静态方法,实现的逻辑就是: - 首先,创建一个 SpringApplication 对象。详细的解析,见 「2.1 构造方法」。- 然后,调用 SpringApplication#run(Class<?> primarySource, String... args) 方法,运行 Spring 应用。。

2.1 构造方法

/**
 * 资源加载器
 */privateResourceLoader resourceLoader;/**
 * 主要的 Java Config 类的数组
 */privateSet<Class<?>> primarySources;/**
 * Web 应用类型
 */privateWebApplicationType webApplicationType;/**
 * ApplicationContextInitializer 数组
 */privateList<ApplicationContextInitializer<?>> initializers;/**
 * ApplicationListener 数组
 */privateList<ApplicationListener<?>> listeners;publicSpringApplication(Class<?>... primarySources){this(null, primarySources);}publicSpringApplication(ResourceLoader resourceLoader,Class<?>... primarySources){this.resourceLoader = resourceLoader;Assert.notNull(primarySources,"PrimarySources must not be null");this.primarySources =newLinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType =WebApplicationType.deduceFromClasspath();// 初始化 initializers 属性setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));// 初始化 listeners 属性setListeners((Collection)getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass =deduceMainApplicationClass();}
  • SpringApplication 的变量比较多,我们先只看构造方法提到的几个。
  • resourceLoader 属性,资源加载器。
  • primarySources 属性,主要的 Java Config 类的数组。在文初提供的示例,就是 MVCApplication 类。
  • webApplicationType属性,调用WebApplicationType.deduceFromClasspath()方法,通过 classpath ,判断 Web 应用类型。- 具体的原理是,是否存在指定的类,已经在 WebApplicationType上的方法添加了注释,直接瞅一眼就明白了。- 这个属性,在下面的 createApplicationContext() 方法,将根据它的值(类型),创建不同类型的 ApplicationContext 对象,即 Spring 容器的类型不同。
  • initializers属性,ApplicationContextInitializer 数组。- 通过 getSpringFactoriesInstances(Class<T> type) 方法,进行获得 ApplicationContextInitializer 类型的对象数组,详细的解析,见 [「2.1.1 getSpringFactoriesInstances」方法。 - 假设只在 Spring MVC 的环境下,initializers 属性的结果如下图:[属性]
  • listeners属性,ApplicationListener 数组。- 也是通过 #getSpringFactoriesInstances(Class<T> type) 方法,进行获得 ApplicationListener 类型的对象数组。- 假设只在 Spring MVC 的环境下,listeners 属性的结果如下图:
  • mainApplicationClass 属性,调用 #deduceMainApplicationClass() 方法,获得是调用了哪个 #main(String[] args) 方法,代码如下:privateClass<?>deduceMainApplicationClass(){try{// 获得当前 StackTraceElement 数组StackTraceElement[] stackTrace =newRuntimeException().getStackTrace();// 判断哪个执行了 main 方法for(StackTraceElement stackTraceElement : stackTrace){if("main".equals(stackTraceElement.getMethodName())){returnClass.forName(stackTraceElement.getClassName());}}}catch(ClassNotFoundException ex){// Swallow and continue}returnnull;}- 在文初的例子中,就是 MVCApplication 类。- 这个 mainApplicationClass 属性,没有什么逻辑上的用途,主要就是用来打印下日志,说明是通过这个类启动 Spring 应用的。

2.1.1 getSpringFactoriesInstances

getSpringFactoriesInstances(Class<T> type)

方法,获得指定类类对应的对象们。代码如下:

private<T>Collection<T>getSpringFactoriesInstances(Class<T> type){returngetSpringFactoriesInstances(type,newClass<?>[]{});}private<T>Collection<T>getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes,Object... args){ClassLoader classLoader =getClassLoader();// Use names and ensure unique to protect against duplicates// ① 加载指定类型对应的,在 `META-INF/spring.factories` 里的类名的数组Set<String> names =newLinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// ② 创建对象们List<T> instances =createSpringFactoriesInstances(type, parameterTypes,
            classLoader, args, names);// ③ 排序对象们AnnotationAwareOrderComparator.sort(instances);return instances;}
  • 处,调用SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)方法,加载指定类型对应的,在META-INF/spring.factories里的类名的数组。- 在 META-INF/spring.factories 文件中,会以 KEY-VALUE 的格式,配置每个类对应的实现类们。
  • 处,调用 #createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) 方法,创建对象们。代码如下:/** * 创建对象的数组 * * @param type 父类 * @param parameterTypes 构造方法的参数类型 * @param classLoader 类加载器 * @param args 参数 * @param names 类名的数组 * @param <T> 泛型 * @return 对象的数组 */private<T>List<T>createSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes,ClassLoader classLoader,Object[] args,Set<String> names){List<T> instances =newArrayList<>(names.size());// 数组大小,细节~// 遍历 names 数组for(String name : names){try{// 获得 name 对应的类Class<?> instanceClass =ClassUtils.forName(name, classLoader);// 判断类是否实现自 type 类Assert.isAssignable(type, instanceClass);// 获得构造方法Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);// 创建对象T instance =(T)BeanUtils.instantiateClass(constructor, args); instances.add(instance);}catch(Throwable ex){thrownewIllegalArgumentException("Cannot instantiate "+ type +" : "+ name, ex);}}return instances;}
  • 处,调用 AnnotationAwareOrderComparator#sort(List<?> list) 方法,排序对象们。例如说,类上有 @Order 注解。

2.2 run

run(String... args)

方法,运行 Spring 应用。代码如下:

publicConfigurableApplicationContextrun(String... args){// ① 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。StopWatch stopWatch =newStopWatch();
    stopWatch.start();//ConfigurableApplicationContext context =null;Collection<SpringBootExceptionReporter> exceptionReporters =newArrayList<>();// ② 配置 headless 属性configureHeadlessProperty();// 获得 SpringApplicationRunListener 的数组,并启动监听SpringApplicationRunListeners listeners =getRunListeners(args);
    listeners.starting();try{// ③ 创建  ApplicationArguments 对象ApplicationArguments applicationArguments =newDefaultApplicationArguments(args);// ④ 加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。ConfigurableEnvironment environment =prepareEnvironment(listeners, applicationArguments);configureIgnoreBeanInfo(environment);// ⑤ 打印 Spring BannerBanner printedBanner =printBanner(environment);// ⑥ 创建 Spring 容器。
        context =createApplicationContext();// ⑦ 异常报告器
        exceptionReporters =getSpringFactoriesInstances(SpringBootExceptionReporter.class,newClass[]{ConfigurableApplicationContext.class}, context);// ⑧ 主要是调用所有初始化类的 initialize 方法prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);// ⑨ 初始化 Spring 容器。refreshContext(context);// ⑩ 执行 Spring 容器的初始化的后置逻辑。默认实现为空。afterRefresh(context, applicationArguments);//  停止 StopWatch 统计时长
        stopWatch.stop();// <12> 打印 Spring Boot 启动的时长日志。if(this.logStartupInfo){newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// <13> 通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。
        listeners.started(context);// <14> 调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。callRunners(context, applicationArguments);}catch(Throwable ex){// <14.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常handleRunFailure(context, ex, exceptionReporters, listeners);thrownewIllegalStateException(ex);}// <15> 通知 SpringApplicationRunListener 的数组,Spring 容器运行中。try{
        listeners.running(context);}catch(Throwable ex){// <15.1> 如果发生异常,则进行处理,并抛出 IllegalStateException 异常handleRunFailure(context, ex, exceptionReporters,null);thrownewIllegalStateException(ex);}return context;}
  • <1> 处,创建 StopWatch 对象,并调用 StopWatch#run() 方法来启动。StopWatch 主要用于简单统计 run 启动过程的时长。
  • <2> 处,配置 headless 属性。这个逻辑,可以无视,和 AWT 相关。
  • <3> 处,调用 #getRunListeners(String[] args) 方法,获得 SpringApplicationRunListener 数组,并启动监听。代码如下:// SpringApplication.javaprivate SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args));}
  • <4> 处,调用 #prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) 方法,加载属性配置。执行完成后,所有的 environment 的属性都会加载进来,包括 application.properties 和外部的属性配置。详细的,先一起跳到 「2.2.1 prepareEnvironment」 中。
  • <5> 处,调用 printBanner(ConfigurableEnvironment environment) 方法,打印 Spring Banner 。效果如下: . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot ::
  • <6> 处,调用 #createApplicationContext() 方法,创建 Spring 容器。详细解析,见 「2.2.2 createApplicationContext」。
  • <7>处,通过#getSpringFactoriesInstances(Class<T> type)方法,进行获得 SpringBootExceptionReporter 类型的对象数组。SpringBootExceptionReporter ,记录启动过程中的异常信息。
  • <8> 处,调用 prepareContext(...) 方法,主要是调用所有初始化类的 initialize(...) 方法。详细解析,见 「2.2.3 prepareContext」 。
  • <9> 处,调用 ``#refreshContext(ConfigurableApplicationContext context)` 方法,启动(刷新) Spring 容器。
  • <10> 处,调用 afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法,执行 Spring 容器的初始化的后置逻辑。默认实现为空。代码如下:protectedvoidafterRefresh(ConfigurableApplicationContext context,ApplicationArguments args){}
  • <11> 处,停止 StopWatch 统计时长。
  • <12> 处,打印 Spring Boot 启动的时长日志。效果如下:2019-01-28 20:42:03.338 INFO 53001 --- [ main] c.iocoder.springboot.mvc.MVCApplication : Started MVCApplication in 20.893 seconds (JVM running for 23.536)
  • <13> 处,调用 SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器启动完成。
  • <14>处,调用#callRunners(ApplicationContext context, ApplicationArguments args)方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。详细解析,见「2.2.5 callRunners」。- <14.1> 处,如果发生异常,则调用 #handleRunFailure(...) 方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。
  • <15>处,调用SpringApplicationRunListeners#running(ConfigurableApplicationContext context)方法,通知 SpringApplicationRunListener 的数组,Spring 容器运行中。- <15.1> 处,如果发生异常,则调用 #handleRunFailure(...) 方法,交给 SpringBootExceptionReporter 进行处理,并抛出 IllegalStateException 异常。

2.2.1 prepareEnvironment

#prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)

方法,加载属性配置。代码如下:

privateConfigurableEnvironmentprepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments){// Create and configure the environment// <1> 创建 ConfigurableEnvironment 对象,并进行配置ConfigurableEnvironment environment =getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());// <2> 通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
    listeners.environmentPrepared(environment);// <3> 绑定 environment 到 SpringApplication 上bindToSpringApplication(environment);// <4> 如果非自定义 environment ,则根据条件转换if(!this.isCustomEnvironment){
        environment =newEnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,deduceEnvironmentClass());}// <5> 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。ConfigurationPropertySources.attach(environment);return environment;}
  • <1> 处,调用 #getOrCreateEnvironment() 方法,创建 ConfigurableEnvironment 对象。代码如下:privateConfigurableEnvironmentgetOrCreateEnvironment(){// 已经存在,则进行返回if(this.environment !=null){returnthis.environment;}// 不存在,则根据 webApplicationType 类型,进行创建。switch(this.webApplicationType){case SERVLET:returnnewStandardServletEnvironment();case REACTIVE:returnnewStandardReactiveWebEnvironment();default:returnnewStandardEnvironment();}}- 根据 webApplicationType 类型,会创建不同类型的 ConfigurableEnvironment 对象。- 例如说,Servlet 需要考虑 <servletContextInitParams /><servletConfigInitParams /> 等配置参数。
  • <1> 处,调用 configureEnvironment(ConfigurableEnvironment environment, String[] args) 方法,配置 environment 变量。代码如下:/** * 是否添加共享的 ConversionService */privateboolean addConversionService =true;protectedvoidconfigureEnvironment(ConfigurableEnvironment environment,String[] args){// <1.1> 设置 environment 的 conversionService 属性if(this.addConversionService){ConversionService conversionService =ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService);}// <1.2> 增加 environment 的 PropertySource 属性源configurePropertySources(environment, args);// <1.3> 配置 environment 的 activeProfiles 属性configureProfiles(environment, args);}- <1.1> 处,设置 environmentconversionService 属性。可以暂时无视。- <1.2> 处,增加 environment 的 PropertySource 属性源。代码如下:/** * 是否添加 JVM 启动参数 */privateboolean addCommandLineProperties =true;/** * 默认的属性集合 */privateMap<String,Object> defaultProperties;protectedvoidconfigurePropertySources(ConfigurableEnvironment environment,String[] args){MutablePropertySources sources = environment.getPropertySources();// 配置的 defaultPropertiesif(this.defaultProperties !=null&&!this.defaultProperties.isEmpty()){ sources.addLast(newMapPropertySource("defaultProperties",this.defaultProperties));}// 来自启动参数的if(this.addCommandLineProperties && args.length >0){String name =CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;if(sources.contains(name)){// 已存在,就进行替换PropertySource<?> source = sources.get(name);CompositePropertySource composite =newCompositePropertySource(name); composite.addPropertySource(newSimpleCommandLinePropertySource("springApplicationCommandLineArgs", args)); composite.addPropertySource(source); sources.replace(name, composite);}else{// 不存在,就进行添加 sources.addFirst(newSimpleCommandLinePropertySource(args));}}}- 代码上可以看出,可以根据配置的 defaultProperties、或者 JVM 启动参数,作为附加的 PropertySource 属性源。- <1.3> 处,配置 environmentactiveProfiles 属性。代码如下:/** * 附加的 profiles 的数组 */privateSet<String> additionalProfiles =newHashSet<>();protectedvoidconfigureProfiles(ConfigurableEnvironment environment,String[] args){ environment.getActiveProfiles();// ensure they are initialized 保证已经被初始化// But these ones should go first (last wins in a property key clash)Set<String> profiles =newLinkedHashSet<>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles()));// 设置 activeProfiles environment.setActiveProfiles(StringUtils.toStringArray(profiles));}- 不了解 Profile 的朋友,可以搜搜看。
  • <2> 处,调用 SpringApplicationRunListeners#environmentPrepared(ConfigurableEnvironment environment) 方法,通知 SpringApplicationRunListener 的数组,环境变量已经准备完成。
  • <3> 处,调用 bindToSpringApplication(ConfigurableEnvironment environment) 方法,绑定 environment 到 SpringApplication 上。暂时不太知道用途。
  • <4> 处,如果非自定义 environment ,则根据条件转换。默认情况下,isCustomEnvironmentfalse ,所以会执行这块逻辑。但是,一般情况下,返回的还是 environment 自身,所以可以无视这块逻辑先。
  • <5> 处,调用 ConfigurationPropertySources#attach(Environment environment)静态方法,如果有 attach 到 environment上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。这块逻辑,也可以先无视。

2.2.2 createApplicationContext

#createApplicationContext()

方法,创建 Spring 容器。代码如下:

/**
 * The class name of application context that will be used by default for non-web
 * environments.
 */publicstaticfinalString DEFAULT_CONTEXT_CLASS ="org.springframework.context."+"annotation.AnnotationConfigApplicationContext";/**
 * The class name of application context that will be used by default for web
 * environments.
 */publicstaticfinalString DEFAULT_SERVLET_WEB_CONTEXT_CLASS ="org.springframework.boot."+"web.servlet.context.AnnotationConfigServletWebServerApplicationContext";/**
 * The class name of application context that will be used by default for reactive web
 * environments.
 */publicstaticfinalString DEFAULT_REACTIVE_WEB_CONTEXT_CLASS ="org.springframework."+"boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";protectedConfigurableApplicationContextcreateApplicationContext(){// 根据 webApplicationType 类型,获得 ApplicationContext 类型Class<?> contextClass =this.applicationContextClass;if(contextClass ==null){try{switch(this.webApplicationType){case SERVLET:
                contextClass =Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:
                contextClass =Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:
                contextClass =Class.forName(DEFAULT_CONTEXT_CLASS);}}catch(ClassNotFoundException ex){thrownewIllegalStateException("Unable create a default ApplicationContext, "+"please specify an ApplicationContextClass", ex);}}// 创建 ApplicationContext 对象return(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);}
  • 根据 webApplicationType 类型,获得对应的 ApplicationContext 对象。

2.2.3 prepareContext

#prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)

方法,准备 ApplicationContext 对象,主要是初始化它的一些属性。代码如下:

privatevoidprepareContext(ConfigurableApplicationContext context,ConfigurableEnvironment environment,SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments,Banner printedBanner){// <1> 设置 context 的 environment 属性
    context.setEnvironment(environment);// <2> 设置 context 的一些属性postProcessApplicationContext(context);// <3> 初始化 ApplicationContextInitializerapplyInitializers(context);// <4> 通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。
    listeners.contextPrepared(context);// <5> 打印日志if(this.logStartupInfo){logStartupInfo(context.getParent()==null);logStartupProfileInfo(context);}// Add boot specific singleton beans// <6> 设置 beanFactory 的属性ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if(printedBanner !=null){
        beanFactory.registerSingleton("springBootBanner", printedBanner);}if(beanFactory instanceofDefaultListableBeanFactory){((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// Load the sources// <7> 加载 BeanDefinition 们Set<Object> sources =getAllSources();Assert.notEmpty(sources,"Sources must not be empty");load(context, sources.toArray(newObject[0]));// <8> 通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。
    listeners.contextLoaded(context);}
  • 这个方法,还是蛮长的,主要是给 context 的属性做赋值,以及 ApplicationContextInitializer 的初始化。
  • 处,设置 contextenvironment 属性。
  • 处,调用 #postProcessApplicationContext(ConfigurableApplicationContext context) 方法,设置 context 的一些属性。代码如下:protectedvoidpostProcessApplicationContext(ConfigurableApplicationContext context){if(this.beanNameGenerator !=null){ context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,this.beanNameGenerator);}if(this.resourceLoader !=null){if(context instanceofGenericApplicationContext){((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);}if(context instanceofDefaultResourceLoader){((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());}}if(this.addConversionService){ context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());}}
  • 处,调用 applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer 。代码如下:protectedvoidapplyInitializers(ConfigurableApplicationContext context){// 遍历 ApplicationContextInitializer 数组for(ApplicationContextInitializer initializer :getInitializers()){// 校验 ApplicationContextInitializer 的泛型非空Class<?> requiredType =GenericTypeResolver.resolveTypeArgument( initializer.getClass(),ApplicationContextInitializer.class);Assert.isInstanceOf(requiredType, context,"Unable to call initializer.");// 初始化 ApplicationContextInitializer initializer.initialize(context);}}- 遍历 ApplicationContextInitializer 数组,逐个调用 ApplicationContextInitializer#initialize(context) 方法,进行初始化。
  • 处,调用 SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器准备完成。
  • 处,打印日志。效果如下:2019-01-2817:53:31.600 INFO 21846---[ main]c.iocoder.springboot.mvc.MVCApplication:StartingMVCApplication on MacBook-Pro-5.local withPID21846(/Users/yunai/Java/spring-boot/spring-boot-tests/spring-boot-yunai-tests/spring-boot-yunai-mvc-tests/target/classes started by yunai in /Users/yunai/Java/spring-boot)2019-01-2817:53:40.028 INFO 21846---[ main]c.iocoder.springboot.mvc.MVCApplication:The following profiles are active: prod- 具体的方法逻辑,胖友自己瞅瞅哈。
  • 处,设置 beanFactory 的属性。
  • 处,调用 #load(ApplicationContext context, Object[] sources) 方法,加载 BeanDefinition 们。代码如下:protectedvoidload(ApplicationContext context,Object[] sources){if(logger.isDebugEnabled()){ logger.debug("Loading source "+StringUtils.arrayToCommaDelimitedString(sources));}// ① 创建 BeanDefinitionLoader 对象BeanDefinitionLoader loader =createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);// ② 设置 loader 的属性if(this.beanNameGenerator !=null){ loader.setBeanNameGenerator(this.beanNameGenerator);}if(this.resourceLoader !=null){ loader.setResourceLoader(this.resourceLoader);}if(this.environment !=null){ loader.setEnvironment(this.environment);}// ③ 执行 BeanDefinition 加载 loader.load();}- 处,调用 getBeanDefinitionRegistry(ApplicationContext context) 方法,创建 BeanDefinitionRegistry 对象。代码如下:privateBeanDefinitionRegistrygetBeanDefinitionRegistry(ApplicationContext context){if(context instanceofBeanDefinitionRegistry){return(BeanDefinitionRegistry) context;}if(context instanceofAbstractApplicationContext){return(BeanDefinitionRegistry)((AbstractApplicationContext) context).getBeanFactory();}thrownewIllegalStateException("Could not locate BeanDefinitionRegistry");}- 处,调用 #createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) 方法,创建 org.springframework.boot.BeanDefinitionLoader 对象。关于它,后续的文章,详细解析。- 处,设置 loader 的属性。- 处,调用 BeanDefinitionLoader#load() 方法,执行 BeanDefinition 加载。
  • 处,调用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的数组,Spring 容器加载完成。

2.2.4 refreshContext

refreshContext(ConfigurableApplicationContext context)

方法,启动(刷新) Spring 容器。代码如下:

/**
 * 是否注册 ShutdownHook 钩子
 */privateboolean registerShutdownHook =true;privatevoidrefreshContext(ConfigurableApplicationContext context){// ① 开启(刷新)Spring 容器refresh(context);// ② 注册 ShutdownHook 钩子if(this.registerShutdownHook){try{
            context.registerShutdownHook();}catch(AccessControlException ex){// Not allowed in some environments.}}}
  • 处,调用 #refresh(ApplicationContext applicationContext) 方法,开启(刷新)Spring 容器。代码如下:protectedvoidrefresh(ApplicationContext applicationContext){// 断言,判断 applicationContext 是 AbstractApplicationContext 的子类Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);// 启动(刷新) AbstractApplicationContext((AbstractApplicationContext) applicationContext).refresh();}- 调用AbstractApplicationContext.refresh()方法,启动(刷新)Spring 容器。
  • 处,调用 ConfigurableApplicationContext#registerShutdownHook() 方法,注册 ShutdownHook 钩子。这个钩子,主要用于 Spring 应用的关闭时,销毁相应的 Bean 们。

2.2.5 callRunners

callRunners(ApplicationContext context, ApplicationArguments args)

方法,调用 ApplicationRunner 或者 CommandLineRunner 的运行方法。代码如下:

privatevoidcallRunners(ApplicationContext context,ApplicationArguments args){// ① 获得所有 Runner 们List<Object> runners =newArrayList<>();// <1.1> 获得所有 ApplicationRunner Bean 们
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());// <1.2> 获得所有 CommandLineRunner Bean 们
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());// <1.3> 排序 runnersAnnotationAwareOrderComparator.sort(runners);// ② 遍历 Runner 数组,执行逻辑for(Object runner :newLinkedHashSet<>(runners)){if(runner instanceofApplicationRunner){callRunner((ApplicationRunner) runner, args);}if(runner instanceofCommandLineRunner){callRunner((CommandLineRunner) runner, args);}}}
  • 处,获得所有 Runner 们,并进行排序。
  • 处,遍历 Runner 数组,执行逻辑。代码如下:private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException("Failed to execute ApplicationRunner", ex); }}private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException("Failed to execute CommandLineRunner", ex); }}

三. 🦁 SpringApplicationRunListeners

org.springframework.boot.SpringApplicationRunListeners

,SpringApplicationRunListener 数组的封装。代码如下:

classSpringApplicationRunListeners{privatefinalLog log;/**
     * SpringApplicationRunListener 数组
     */privatefinalList<SpringApplicationRunListener> listeners;SpringApplicationRunListeners(Log log,Collection<?extendsSpringApplicationRunListener> listeners){this.log = log;this.listeners =newArrayList<>(listeners);}publicvoidstarting(){for(SpringApplicationRunListener listener :this.listeners){
            listener.starting();}}publicvoidenvironmentPrepared(ConfigurableEnvironment environment){for(SpringApplicationRunListener listener :this.listeners){
            listener.environmentPrepared(environment);}}publicvoidcontextPrepared(ConfigurableApplicationContext context){for(SpringApplicationRunListener listener :this.listeners){
            listener.contextPrepared(context);}}publicvoidcontextLoaded(ConfigurableApplicationContext context){for(SpringApplicationRunListener listener :this.listeners){
            listener.contextLoaded(context);}}publicvoidstarted(ConfigurableApplicationContext context){for(SpringApplicationRunListener listener :this.listeners){
            listener.started(context);}}publicvoidrunning(ConfigurableApplicationContext context){for(SpringApplicationRunListener listener :this.listeners){
            listener.running(context);}}publicvoidfailed(ConfigurableApplicationContext context,Throwable exception){for(SpringApplicationRunListener listener :this.listeners){callFailedListener(listener, context, exception);}}privatevoidcallFailedListener(SpringApplicationRunListener listener,ConfigurableApplicationContext context,Throwable exception){try{
            listener.failed(context, exception);}catch(Throwable ex){if(exception ==null){ReflectionUtils.rethrowRuntimeException(ex);}if(this.log.isDebugEnabled()){this.log.error("Error handling failed", ex);}else{String message = ex.getMessage();
                message =(message !=null)? message :"no error message";this.log.warn("Error handling failed ("+ message +")");}}}}

3.1 SpringApplicationRunListener

org.springframework.boot.SpringApplicationRunListener

,SpringApplication 运行的监听器接口。代码如下:

publicinterfaceSpringApplicationRunListener{/**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     */voidstarting();/**
     * Called once the environment has been prepared, but before the
     * {@link ApplicationContext} has been created.
     * @param environment the environment
     */voidenvironmentPrepared(ConfigurableEnvironment environment);/**
     * Called once the {@link ApplicationContext} has been created and prepared, but
     * before sources have been loaded.
     * @param context the application context
     */voidcontextPrepared(ConfigurableApplicationContext context);/**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * @param context the application context
     */voidcontextLoaded(ConfigurableApplicationContext context);/**
     * The context has been refreshed and the application has started but
     * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
     * ApplicationRunners} have not been called.
     * @param context the application context.
     * @since 2.0.0
     */voidstarted(ConfigurableApplicationContext context);/**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
     * {@link ApplicationRunner ApplicationRunners} have been called.
     * @param context the application context.
     * @since 2.0.0
     */voidrunning(ConfigurableApplicationContext context);/**
     * Called when a failure occurs when running the application.
     * @param context the application context or {@code null} if a failure occurred before
     * the context was created
     * @param exception the failure
     * @since 2.0.0
     */voidfailed(ConfigurableApplicationContext context,Throwable exception);}

目前,SpringApplicationRunListener 的实现类,只有 EventPublishingRunListener 类。

3.2 EventPublishingRunListener

org.springframework.boot.context.event.EventPublishingRunListener

,实现 SpringApplicationRunListener、Ordered 接口,将 SpringApplicationRunListener 监听到的事件,转换成对应的 SpringApplicationEvent 事件,发布到监听器们。

代码如下:


public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    /**
     * Spring 应用
     */
    private final SpringApplication application;
    /**
     * 参数集合
     */
    private final String[] args;
    /**
     * 事件广播器
     */
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        // 创建 SimpleApplicationEventMulticaster 对象
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        // 添加应用的监听器们,到 initialMulticaster 中
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

    @Override // ApplicationEnvironmentPreparedEvent
    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
    }

    @Override // ApplicationContextInitializedEvent
    public void contextPrepared(ConfigurableApplicationContext context) {
        this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
    }

    @Override // ApplicationPreparedEvent
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
    }

    @Override // ApplicationStartedEvent
    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
    }

    @Override // ApplicationFailedEvent
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        } else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }

    private static class LoggingErrorHandler implements ErrorHandler {

        private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

        @Override
        public void handleError(Throwable throwable) {
            logger.warn("Error calling ApplicationEventListener", throwable);
        }

    }

}
  • 代码比较简单,自己瞅瞅就明白了。
  • 通过这样的方式,可以很方便的将 SpringApplication 启动的各种事件,方便的修改成对应的 SpringApplicationEvent 事件。这样,我们就可以不需要修改 SpringApplication 的代码。或者说,我们认为 EventPublishingRunListener 是一个“转换器”。

四. 🦁 总结

整块代码略微有点长,一定一定一定自己调试下。总的来说,逻辑并不复杂,是吧?是吧!

标签: java Springboot

本文转载自: https://blog.csdn.net/m0_58847451/article/details/130682692
版权归原作者 狮子也疯狂 所有, 如有侵权,请联系我们删除。

“【SpringBoot源码剥析】| SpringApplication”的评论:

还没有评论