目录
一. 🦁 概述
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>
处,设置environment
的conversionService
属性。可以暂时无视。-<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>
处,配置environment
的activeProfiles
属性。代码如下:/** * 附加的 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
,则根据条件转换。默认情况下,isCustomEnvironment
为false
,所以会执行这块逻辑。但是,一般情况下,返回的还是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 的初始化。 ①
处,设置context
的environment
属性。②
处,调用#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 是一个“转换器”。
四. 🦁 总结
整块代码略微有点长,一定一定一定自己调试下。总的来说,逻辑并不复杂,是吧?是吧!
版权归原作者 狮子也疯狂 所有, 如有侵权,请联系我们删除。