内容简介:调用SpringApplication.run方法先执行SpringApplication的构造方法,进行初始化动作,包括:接下来看一下run做了什么
@Slf4j @EnableDiscoveryClient @SpringBootApplication public class KeplerPostLoanApplication { /** * 项目启动类 * * @param args 启动参数 */ public static void main(String[] args) { SpringApplication.run(KeplerPostLoanApplication.class, args) } } 复制代码
调用SpringApplication.run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); } 复制代码
先执行SpringApplication的构造方法,进行初始化动作,包括:
- 1、识别webApplicationType类型
- 2、识别mainApplicationClass类
- 3、加载META-INF/spring.factories文件中定义的定义的 ApplicationContextInitializer、ApplicationListener
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 加载META-INF/spring.factories文件中,定义的ApplicationContextInitializer setInitializers((Collection)getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 加载META-INF/spring.factories文件中,定义的ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); } 复制代码
接下来看一下run做了什么
/** * 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(); // 获取SpringApplicationRunListener // 默认只有EventPublishingRunListener,用来结合spring启动流程,发布SpringApplicationEvent SpringApplicationRunListeners listeners = getRunListeners(args); // 发布ApplicationStartingEvent,例如 // BackgroundPreinitializer listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); // 子类扩展 afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } // 发布ApplicationStartedEvent,例如 // TomcatMetricsBinder listeners.started(context); // 回调ApplicationRunner、CommandLineRunner,例如 // JobLauncherCommandLineRunner,启动jobLauncher callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; } 复制代码
归纳下SpringApplication.run方法的关键动作
- 第一步:构造容器环境(prepareEnvironment)
- 第二步:创建容器(createApplicationContext)
- 第三步:准备容器(prepareContext)
- 第四步:刷新容器(refreshContext)
- 第五步:刷新容器后的扩展接口(afterRefresh)
- 第六步:回调Runner类(callRunners)
其中:执行过程中,通过SpringApplicationRunListener的实现类EventPublishingRunListener在对应动作的时间点,Spring启动事件。
附上,springApplicationEvent事件列表
主要以上6步,下面具体分析。
第一步:构造容器环境
/** SpringApplication.java **/ private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 1、初始化environment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2、加载默认配置,defaultProperties、springApplicationCommandLineArgs configureEnvironment(environment, applicationArguments.getSourceArgs()); // 3.发布ApplicationEnvironmentPreparedEvent,例如 // BootstrapApplicationListener,创建bootstrapContext上下文,加载bootstrap.properties // ConfigFileApplicationListener,加载YamlProperty文件与PropertiesProperty文件,添加PropertySourceOrderingPostProcessor listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } 复制代码
首先getOrCreateEnvironment方法实现如下: new 一个StandardXXXEnvironment(),其中,构造函数中会执行customizePropertySources方法,加载对应的PropertySource。
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); } } public AbstractEnvironment() { customizePropertySources(this.propertySources); } 复制代码
附上StandardEnvironment的customizePropertySources实现
@Override protected void customizePropertySources(MutablePropertySources propertySources) { // 属性获取时,for each propertySources,先匹配到就返回, // 所以systemProperties(如:java -Dsource=xxx -jar xxx.jar)优先级高于systemEnvironment(环境变量) propertySources.addLast(new MapPropertySource("systemProperties", getSystemProperties())); propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment())); } 复制代码
第二步:创建容器
实际上,就是new AnnotationConfigServletWebServerApplicationContext()
// SpringApplication.java protected ConfigurableApplicationContext createApplicationContext() { 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) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); } 复制代码
重点关注:构造方法中, 创建了AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner两个对象
// AnnotationConfigServletWebServerApplicationContext.java public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } 复制代码
其中,AnnotatedBeanDefinitionReader 创建过程中,注册了多个十分重要的BeanPostProcessor,包括处理@Configuration注解的ConfigurationClassPostProcessor等。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); // Register all relevant annotation post processors in the given registry,包括: // ConfigurationClassPostProcessor // AutowiredAnnotationBeanPostProcessor // RequiredAnnotationBeanPostProcessor // CommonAnnotationBeanPostProcessor // PersistenceAnnotationBeanPostProcessor // EventListenerMethodProcessor // DefaultEventListenerFactory AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } 复制代码
第三步:准备容器
/** SpringApplication.java **/ private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); // 调用ApplicationContextInitializer.initialize(),例如: // EnvironmentDecryptApplicationInitializer // PropertySourceBootstrapConfiguration // SharedMetadataReaderFactoryContextInitializer,注入SharedMetadataReaderFactoryBean applyInitializers(context); // ApplicationContextInitializedEvent,暂无例子 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加载springBoot启动类注入到spring容器中bean map中 // AnnotatedBeanDefinitionReader.doRegisterBean() load(context, sources.toArray(new Object[0])); // 发布ApplicationPreparedEvent // ConfigFileApplicationListener,注入PropertySourceOrderingPostProcessor,调整defaultProperties到尾部 listeners.contextLoaded(context); } 复制代码
第四步:刷新容器
refresh方法在spring整个源码体系中举足轻重,后续单独讲解。
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } @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. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } 复制代码
第五步:刷新容器后的扩展接口(afterRefresh)
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { } 复制代码
扩展接口,设计模式中的模板方法,默认为空实现,子类扩展。
第六步:回调Runner类(callRunners)
容器就绪后,触发回调动作,目前见到的实现有 JobLauncherCommandLineRunner,启动jobLauncher
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } 复制代码
本章节常用扩展接口以及使用案例
执行顺序(重点关注):
- ApplicationStartingEvent
- ApplicationEnvironmentPreparedEvent
- ApplicationContextInitializer
- ApplicationContextInitializedEvent
- ApplicationPreparedEvent
- ApplicationStartedEvent
- ApplicationRunner、CommandLineRunner
- ApplicationReadyEvent
使用案例:
1. ApplicationStartingEvent
- 简单描述:程序启动时,最早触发的动作
- 应用场景:
- ApplicationPidFileWriter(saves application PID into file)
- LoggingApplicationListener(configures the {@link LoggingSystem})
2. ApplicationEnvironmentPreparedEvent
- 简单描述: 创建环境Environment完成,并且已经载默认的配置,例如systemEnvironment、systemProperties
- 应用场景:比较多的是加载自定义的配置、spring Cloud也通过这个事件,去初始化Cloud上下文。
- BootstrapApplicationListener:初始化BootstrapApplicationContext上下文,包括加载bootstrap.properties、载入spring.factories 定义的BootstrapConfiguration
- ConfigFileApplicationListener:加载application.properties、application.yml
以上所述就是小编给大家介绍的《springBoot2.1.5启动源码解析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。