专业的编程技术博客社区

网站首页 > 博客文章 正文

Springboot源码学习4 SpringApplication类的run方法 part3

baijin 2024-08-15 00:38:31 博客文章 9 ℃ 0 评论

示例代码

示例代码克隆地址如下,分支为develop。

https://gitee.com/kutilion/MyArtifactForEffectiveJava.git

正文

Springboot服务启动调用了如下的run方法。 这个方法的内容比较多,分几部分来学习。今天学习标注的⑥和⑦。

⑥ 获取异常分析器

⑦ 准备上下文

	public ConfigurableApplicationContext run(String... args) {
 ...
 // ⑥
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
 // ⑦
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
 ...
	}

⑥获取异常分析器

getSpringFactoriesInstances可以看成sprigboot的资源加载器,从META-INF/spring.factories中加载指定的Bean。首先加载的是FailureAnalyzers,它定义在spring-boot-2.1.3.RELEASE.jar的spring.factories中。通过FailureAnalyzers类的构造方法调用SpringFactoriesLoader.loadFactoryNames再次读取spring-boot-autoconfigure-2.1.3.RELEASE.jar包以及spring-boot-2.1.3.RELEASE.jar包spring.factories中所有FailureAnalyzer的名字并且进行实例化, 实例化之后装在FailureAnalyzers的列表属性内部。

Springboot提供的异常分析器大约有17种,基本涵盖了经常出现的异常等。个人感觉这个功能主要是为了出现错误的时候分析并且跟踪。

⑦准备上下文

先看一下prepareContext方法的代码

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
 // 给上下文设置环境,环境包括各种环境变量及配置
		context.setEnvironment(environment);
 // 上下文后处理
		postProcessApplicationContext(context);
 // 在刷新上下文之前,设定一些初始化器
		applyInitializers(context);
 // 这个方法没有实装,可能为了扩展。
		listeners.contextPrepared(context);
 // 启动日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
 // 注册特定单例bean
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
 // 判断是否打印banner
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
 // 是否允许覆盖同名的单例bean,参数this.allowBeanDefinitionOverriding可以在run方法执行前设定
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	
 // 获取资源primarySources
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
 // 将bean加载到上下文
		load(context, sources.toArray(new Object[0]));
 // 向上下文中添加ApplicationListener,并广播ApplicationPreparedEvent事件
		listeners.contextLoaded(context);
	}

setEnvironment

这个方法讲SpringApplication生成environment设定到上下文种。上一章种提过的AnnotatedBeanDefinitionReader生成的新environment也在这里被替换成SpringApplication生成的environment。原因是我们使用了Springboot,SpringApplication生成environment支持Springboot。AnnotatedBeanDefinitionReader生成是默认的环境, 无法满足要求。是为了支持不使用Springboot的项目生成的。

postProcessApplicationContext

这个方法顾名思义是上下文的后处理。但是其中的前两个if语句因判断条件this.beanNameGenerator != null和this.resourceLoader != null都是false,不起作用。第三个if判断条件this.addConversionService是true,主要是将默认的convert(如StringToTimeZoneConverter)和formater(如CurrencyUnitFormatter)都注册进来。这个addConversionService是SpringApplication类的成员变量,默认为true。

applyInitializers

初始化器,是在建立SpringApplication的时候通过扫描spring.factories添加进来的。 具体可见SpringApplication类的构造方法种调用的setInitializers方法。这里调用了所有初始化器的initialize方法进行各种功能的初始化。通过debug调试或者查看spring-boot-2.1.3.RELEASE.jar和spring-boot-autoconfiguration-2.13.RELEASE.jar包的spring.factories文件, 可以发现一共有六个初始化器会被执行。

①DelegatingApplicationContextInitializer

当环境种有属性context.initializer.classes定义的初始化器(一般是用户自定义)的时候,这个类作为这些初始化器的代理, 来执行自定义初始化器的初始化方法。

②ContextIdApplicationContextInitializer

当环境种有属性spring.application.name定义的名字的时候,取得这个值设定为application的id。如果不存在则使用默认的名字"application"。

③ConfigurationWarningsApplicationContextInitializer

向上下文中注册了一个类型为ConfigurationWarningsPostProcessor的BeanFactoryPostProcessor。BeanFactoryPostProcessor在底层bean创建之后执行一些操作。这里是底层bean注册如果出现了警告则输出这些警告。

④ServerPortInfoApplicationContextInitializer

与其说这是一个初始化器,倒不如说这是一个监听器。它的构造方法将自己作为监听器加入到了上下文中。当触发事件WebServerInitializedEvent时它将真实访问的服务器端口注册到环境的local.server.port变量中,同时也注册到上下文中。如果当前上下文有父类,也会注册到父类中。其中的server是可指定的。这样,服务器端口就可以直接注解@Value或者environment.getProperty来使用。

⑤SharedMetadataReaderFactoryContextInitializer

它注册了CachingMetadataReaderFactory并且配置了ConfigurationClassPostProcessor。

⑥ConditionEvaluationReportLoggingListener

将ConditionEvaluationReport写入到日志,使用DEBUG或者INFO级别输出。这个伪装成初始化器的监听器监听两个事件ContextRefreshedEvent以及ApplicationFailedEvent。初始化失败或者出现不可预测的运行时异常时,会输出日志。

beanFactory.registerSingleton

从控制台输入的参数中得到需要注册的单例bean并且注册。

load(context, sources.toArray(new Object[0]));

创建了一个BeanDefinitionLoader对象;BeanDefinitionLoader中有成员变量AnnotatedBeanDefinitionReader,XmlBeanDefinitionReader和ClassPathBeanDefinitionScanner,通过这三个成员变量从底层源加载bean定义。跟进的话,可以发现实际上调用了BeanDefinitionLoader类的方法private int load(Object source)。可知一共有四种资源可以被加载:Class、Resource、Package和CharSequence四种,加载方式也不相同。

因为本例中只有一个资源就是工程本身,加载的时候是通过Class方式加载的。加载之前会判断是否有@Component注解,如果有则封装成一个名字为工程名(bootApplication)的BeanDefinition对象,并将其注册到beanFactory的BeanDefinitionMap中。

listeners.contextLoaded(context);

通过单步调试会发现当前的变量listners中只有一个元素EventPublishingRunListener。并且调用了它的contextLoaded方法,为起内部的监听器传递了上下文对象。这时其内部的监听器拥有上下文的引用。之后contextLoaded方法广播了ApplicationPreparedEvent事件。这会触发所有接受这个事件的监听器的onApplicationEvent方法。默认会触发如下5个监听器,

  • ConfigFileApplicationListener
  • LoggingApplicationListener
  • BackgroundPreinitializer
  • DelegatingApplicationListener
  • ApplicationPreparedEventListener

其中最后一个是示例中自定义的监听器。

总结

漫长的一章,每天花费30分钟到一小时差不多用了一个星期时间。初步了解了上下文是怎样准备的。尤其是单例bean的加载(load),详细了解才知道springboot是如何通过注解或者xml配置文件来加载bean的。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表