springBoot2.1.5启动源码解析(一)

栏目: Java · 发布时间: 5年前

内容简介:调用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事件列表

springBoot2.1.5启动源码解析(一)

主要以上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);
		}
	}
}
复制代码

本章节常用扩展接口以及使用案例

执行顺序(重点关注):

  1. ApplicationStartingEvent
  2. ApplicationEnvironmentPreparedEvent
  3. ApplicationContextInitializer
  4. ApplicationContextInitializedEvent
  5. ApplicationPreparedEvent
  6. ApplicationStartedEvent
  7. ApplicationRunner、CommandLineRunner
  8. 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启动源码解析(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux内核设计的艺术

Linux内核设计的艺术

新设计团队 / 机械工业出版社华章公司 / 2011-6-20 / 79.00元

关于Linux内核的书已经不计其数,但这本书却是独树一帜的,它的内容代表着Linux内核研究成果的世界顶尖级水平,它在世界范围内首次提出并阐述了操作系统设计的核心指导思想——主奴机制,这是所有操作系统研究者的一笔宝贵财富。本书可能也代表着同类图书的顶尖水平,是一本真正能引导我们较为容易地、极为透彻地理解Linux内核的经典之作,也可能是当前唯一能从本质上指引我们去设计和开发拥有自主知识产权的操作系......一起来看看 《Linux内核设计的艺术》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具