SpringBoot源码解析三部曲(三)——运行流程

关联文章:
SpringBoot源码解析三部曲(一)——自动配置
SpringBoot源码解析三部曲(二)——SpringApplication实例化

1、SpringApplication run方法简介

在上一篇已经介绍了,当SpringApplication对象被创建之后,通过调用其run方法进行Spring Boot的启动和运行,正式开启SpringApplication的生命周期。

SpringApplication调用的run方法的核心操作如下图所示:
run方法流程

对照流程图,来整体看一下run方法的源码:

public ConfigurableApplicationContext run(String... args) {
		// 创建StopWatch对象, 用于统计run方法启动时长
		StopWatch stopWatch = new StopWatch();
		// 启动统计
		stopWatch.start();
  		// 应用上下文对象,Spring Boot使用的应用上下文是ConfigurableApplicationContext
		ConfigurableApplicationContext context = null;
  		// 异常报告器
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置headless属性
		configureHeadlessProperty();
		/**
		 * 获得SpringApplicationRunListener数组, 并将其封装在变量listeners中
		 * SpringApplicationRunListener接口是监听SpringApplication中run方法各个执行阶段的, 提供了一系列的方法, 用户可以通过回调这些方法, 在启动各个阶段时加入指定的逻辑处理
		 */
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 启动监听器, 里面的操作为遍历SpringApplicationRunListener数组每个元素, 并执行
		listeners.starting();
		try {
			// 初始化ApplicationArguments对象, ApplicationArguments是用于提供访问运行SpringApplication时的参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 初始化ConfigurableEnvironment, 加载属性配置, 包括所有的配置属性(如: application.properties中和外部的属性配置)
			// ConfigurableEnvironment接口的主要作用是提供当前运行环境的公开接口, 比如配置文件profiles各类系统属性和变量的设置, 添加, 读取, 合并
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 对环境中忽略信息配置项spring.beaninfo.ignore的值进行判断, 如果为true, 跳过BeanInfo类的扫描
			configureIgnoreBeanInfo(environment);
			// 打印Bannner
			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);
			}
			// 通知监听器: 容器启动完成
			listeners.started(context);
			// 调用ApplicationRunner和CommandLineRunner的运行方法, 用来给用户实现一些在容器启动时的自定义操作
			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;
	}

2、SpringApplicationRunListener监听器

2.1、SpringApplicationRunListeners容器

从上述源码中可以看到,除了计时统计的功能,第一步就是监听器SpringApplicationRunListeners的获取和使用。SpringApplicationRunListeners是一个SpringApplicationRunListener的容器,它将SpringApplicationRunListener的集合以构造方法传入,并赋值给其成员变量listeners,然后提供了针对listeners成员变量的各种遍历操作方法。源码如下:

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

	void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}
}

SpringApplicationRunListeners的构建很简单,run方法中使用的是getRunListeners私有方法,源码如下:

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		// 构造Class数组
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 调用SpringApplicationRunListeners的构造方法
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

	/**
	 * 用来获取spring.factories配置文件中的注册类, 并进行实例化操作
	 */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// 获取类加载器
		ClassLoader classLoader = getClassLoader();
		// 加载META-INF/spring.factories中对应的配置, 并将结果存储于Set中, 方便去重
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 创建实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

	/**
	 * 实例化注册类
	 */
	@SuppressWarnings("unchecked")
	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		// 遍历加载到的类名(全限定类名)
		for (String name : names) {
			try {
				// 使用全路径类名通过反射获取class对象
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				// 获取有参构造器
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				// 执行构造函数获取实例
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				// 将实例加入到返回结果中
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

getSpringFactoriesInstances方法在前两篇也介绍了很多次了,通过SpringFactoriesLoaderloadFactoryNames方法加载META-INF/spring.factories中对应的配置。SpringApplicationRunListener的配置只有一个——EventPublishingRunListener。

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

2.2、SpringApplicationRunListener解析

接口SpringApplicationRunListener是SpringApplication的run方法监听器,提供了一系列的方法,用户可以通过回调这些方法,在启动的各个流程加入指定的逻辑处理。源码如下

public interface SpringApplicationRunListener {

	// 当run方法第一次被执行时, 会被立即调用, 可用于非常早期的初始化工作
	default void starting() {
	}

	// 当environment准备完成, 在ApplicationContext容器创建之前, 该方法被调用
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	// 当ApplicationContext构建完成, 资源还未被加载时, 该方法被调用
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	// 当ApplicationContext资源加载完成, 未被刷新之前, 该方法被调用
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	// 当ApplicationContext刷新并启动之后, CommandLineRunner和ApplicationRunner未被调用之前, 该方法被调用
	default void started(ConfigurableApplicationContext context) {
	}

	// 当所有准备工作就绪, run方法执行完成之前, 该方法被调用
	default void running(ConfigurableApplicationContext context) {
	}

	// 当应用程序出现错误时, 该方法被调用
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}
}

从源码中可以看出,接口SpringApplicationRunListener为run方法提供了各个运行阶段的监听事件处理功能,执行时机如下图所示:
在这里插入图片描述

2.2.1、实现类EventPublishingRunListener解析

从META-INF/spring.factories配置文件中可以看出,SpringApplicationRunListener只有一个默认实现类EventPublishingRunListener,EventPublishingRunListener使用内置的SimpleApplicationEventMulticaster来广播在应用上下文刷新之前触发的事件。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	// 事件广播器
	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		// 创建并设置广播器
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		// 遍历ApplicationListener,并关联SimpleApplicationEventMulticaster广播器
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
  
  	// 当run方法第一次被执行时,该方法被调用
  	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

  	// 当environment准备完成,在ApplicationContext容器创建之前,该方法被调用
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

  	// 当ApplicationContext容器构建完成,资源还未被加载时,该方法被调用
	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

  	// 当ApplicationContext容器资源加载完成,未被刷新之前,该方法被调用
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		// 遍历application中所有的监听器
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			// 如果为ApplicationContextAware, 则将上下文信息设置到该监听器内
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			// 将application中的监听器全部添加到上下文中
			context.addApplicationListener(listener);
		}
		// 广播事件ApplicationPreparedEvent
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

  	// 当ApplicationContext容器刷新并启动后,CommandLineRunner和ApplicationRunner未被调用之前,该方法被调用
	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

  	// 当所有准备工作就绪,run方法执行完成之前,该方法被调用
	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

  	// 当应用程序出现错误时, 该方法被调用
	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			context.publishEvent(event);
		}
		else {
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}
}

Spring Boot完成基本的初始化之后,会遍历SpringApplication的所有ApplicationListener实例,并将它们与SimpleApplicationEventMulticaster进行关联,方便SimpleApplicationEventMulticaster后续将事件传递给所有的监听器。

EventPublishingRunListener针对不同的事件的处理流程基本相同,可以概括为以下几个步骤:

  1. 当run方法运行到某个阶段时,调用EventPublishingRunListener的某个方法。
  2. EventPublishingRunListener的某个方法将application参数和args参数封装到对应的事件中,这里的事件均为SpringApplicationEvent的实现类。
  3. 通过成员变量initialMulticaster的multicastEvent方法对事件进行广播,或者通过该方法参数ConfigurableApplicationContext的publishEvent来对事件进行发布。
  4. 对应的ApplicationListener被触发,执行响应的业务逻辑。

从上述步骤中可以看出,有些方法时通过成员变量initialMulticastermulticastEvent方法对事件进行广播,有些方法是通过参数ConfigurableApplicationContextpublishEvent来对事件进行发布。从这些方法中可以找到contextLoaded这个方法,它是两种不同事件广播形式的分水岭。contextLoaded方法在发布事件之前做了两件事:一,遍历application的所有监听器实现类,如果该实现类还实现了ApplicationContextAware接口,则将上下文信息设置到该监听器内;二、将application中的监听器实现类全部添加到上下文中。最后调用事件广播。在contextLoaded方法执行之前,上下文还没有初始化完成,所以无法通过它的publishEvent方法进行事件发布。

3、初始化ApplicationArguments

在run方法源码中,监听器启动之后,紧接着便是执行ApplicationArguments对象的初始化,ApplicationArguments是用于提供访问运行SpringApplication时的参数。

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

public DefaultApplicationArguments(String... args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
}

在DefaultApplicationArguments将参数args封装为Source对象,Source对象是基于Spring框架的SimpleCommandLinePropertySource来实现的。

4、初始化ConfigurableEnvironment

完成ApplicationArguments参数的准备之后,便开始通过prepareEnvironment方法对ConfigurableEnvironment对象进行初始化操作。ConfigurableEnvironment接口的主要作用是提供当前运行环境的公开接口,比如配置文件profiles各类系统属性和变量的设置、添加、读取、合并等功能。

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// 获取或创建环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置环境, 主要包括PropertySource和activeProfiles的配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 将ConfigurationPropertySources附加到指定环境中的第一位, 并动态跟踪环境的添加或删除
		ConfigurationPropertySources.attach(environment);
		// 通知监听器listener环境准备完成
		listeners.environmentPrepared(environment);
		// 将环境绑定到SpringApplication, 将环境绑定到name为spring.main的目标上
		bindToSpringApplication(environment);
		// 判断是否定制的环境, 如果不是定制的则将环境转换到StandardEnvironment
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		// 再次将ConfigurationPropertySources附加到指定环境中的第一位, 并动态跟踪环境的添加或删除
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

4.1、获取或创建环境

该方法比较简单,如果environment存在,则直接返回。如果environment不存在,则根据SpringApplication实例化时推断的webApplicationType来进行区分创建环境。

	private ConfigurableEnvironment getOrCreateEnvironment() {
		// 如果environment存在, 则直接返回
		if (this.environment != null) {
			return this.environment;
		}
		// 根据前面推断的webApplicationType, 创建不同的环境实现
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

4.2、配置环境

在获得环境变量对象之后,开始对环境变量和参数进行相应的设置,主要包括转换服务的设置、PropertySources的设置和activeProfiles的设置。

	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		// 如果为true则获取并设置转换服务
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		// 配置PropertySources
		configurePropertySources(environment, args);
		// 配置Profiles
		configureProfiles(environment, args);
	}

首先判断addConversionService变量是否为true,也就是判断是否需要添加转换服务,如果需要,则获取转换服务实例,并对环境设置转换服务。随后进行PropertySources和Profiles的配置。

	// 配置PropertySources
	protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    	// 获取环境中的属性资源信息
		MutablePropertySources sources = environment.getPropertySources();
    	// 如果默认属性配置存在则将其放置于属性资源的最后位置
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
			sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
		}
    	// 如果命令行属性存在
		if (this.addCommandLineProperties && args.length > 0) {
      		// 常量值为:commandLineArgs
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
      		// 如果默认属性资源中不包含该命令,则将命令行属性放置在第一位,如果包含,则通过CommandLinePropertySource进行处理
			if (sources.contains(name)) {
				PropertySource<?> source = sources.get(name);
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(
						new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			}
			else {
        		// 放置在第一位
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}

主要是对参数的优先级进行处理,首先,如果存在默认属性配置,则将默认属性配置放置在最后,也就是优先级最低。对于命令参数,如果命令参数已经存在于属性配置中,则使用CompositePropertySource类进行相同name的参数处理;如果命令参数并不存在于属性配置中,则直接将其设置为优先级最高。

protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    	// 如果存在额外的Profiles,则将其放置在第一位,随后再获得其他的Profiles
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}

5、忽略信息配置

在上述完成ConfigurableEnvironment的初始化之后,程序又对环境中的忽略信息配置项(参数spring.beaninfo.ignore)的值进行判断,进而设置为系统参数中的忽略项。

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    	// 如果系统参数中spring.beaninfo.ignore为null
		if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
      		// 获取环境中spring.beaninfo.ignore的数量
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
      		// 设置对应的系统参数
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
		}
	}

spring.beaninfo.ignore的配置用来决定是否跳过BeanInfo类的扫描,如果设置为true,则跳过。

6、打印Banner

完成环境的基本处理之后,下面就是控制台Banner的打印了。

	private Banner printBanner(ConfigurableEnvironment environment) {
    	// 如果处于关闭状态,则返回null
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
    	// 如果resourceLoader不存在则创建一个默认的ResourceLoader
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader());
    	// 创建SpringApplicationBannerPrinter
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    	// 打印到日志中
		if (this.bannerMode == Mode.LOG) {
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
    	// 打印到控制台
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

7、创建应用上下文

根据推断的Web应用类型,创建对应的上下文。

	public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";

	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

	public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";	

	protected ConfigurableApplicationContext createApplicationContext() {
		// 获取容器中的类变量
		Class<?> contextClass = this.applicationContextClass;
		// 如果为null, 则根据Web应用类型按照默认类进行创建
		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);
			}
		}
    	// 如果存在对应的Class配置,则通过Spring提供的BeanUtils来进行实例化
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

8、准备应用上下文

完成了应用上下文的创建,SpringApplication通过prepareContext方法来进行应用上下文的准备,核心功能和流程如下图所示:
在这里插入图片描述
结合流程图,看一下prepareContext方法的源码:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		// --------------------- 应用上下文准备阶段 ---------------------
		// 设置上下文的配置环境
		context.setEnvironment(environment);
		// 应用上下文后置处理
		postProcessApplicationContext(context);
		// 在上下文刷新之前, ApplicationContextInitializer初始化上下文
		applyInitializers(context);
		// 通知监听器上下文准备完成
		listeners.contextPrepared(context);

		// --------------------- 应用上下文加载阶段 ---------------------
		// 打印日志, 启动Profile
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 获取ConfigurableListableBeanFactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 将beanFactory注册为单例对象
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			// 注册打印日志对象
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			// 设置是否允许覆盖注册
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// 获取全部配置源, 其中包含primarySources(被@SpringBootApplication注解或@EnableAutoConfiguration注解注释的类)和sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 将配置源中的Bean加载到上下文中
		load(context, sources.toArray(new Object[0]));
		// 通知监听器上下文加载完成
		listeners.contextLoaded(context);
	}

从代码中可以看出,准备应用上下文分为两个阶段,准备阶段、加载阶段,下面依次来看下这两个阶段。

8.1、应用上下文准备阶段

在上下文准备阶段,主要有3步操作:对应用上下文Context设置environment、应用上下文后置处理、ApplicationContextInitializer初始化Context。

8.1.1、对应用上下文Context设置environment

	public void setEnvironment(ConfigurableEnvironment environment) {
    	// 设置Context的environment
		super.setEnvironment(environment);
    	// 设置Context的reader属性的conditionEvaluator属性
		this.reader.setEnvironment(environment);
    	// 设置Context的scanner属性的environment属性
		this.scanner.setEnvironment(environment);
	}

8.1.2、应用上下文后置处理

设置beanNameGenerator、resourceLoader、classLoader、conversionService,在此阶段,beanNameGenerator和resourceLoader都为null,实际上只做了最后一步的设置转换服务。

	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
			// 如果beanNameGenerator不为null, 则将当前的beanNameGenerator按照默认名字进行注册
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		// 如果resourceLoader不为null, 则根据上下文的类型分别进行ResourceLoader和ClassLoader的设置
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		// 如果为true则获取并设置转换服务
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}

8.1.3、对应用上下文初始化

	protected void applyInitializers(ConfigurableApplicationContext context) {
		// 获取ApplicationContextInitializer集合并遍历
		for (ApplicationContextInitializer initializer : getInitializers()) {
			// 解析当前遍历的initializer实现ApplicationContextInitializer的泛型类型
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			// 判断泛型是否和ConfigurableApplicationContext匹配
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			// 初始化上下文
			initializer.initialize(context);
		}
	}

8.2、应用上下文加载阶段

在上下文加载阶段,主要有5步操作:打印日志和Profile的设置、设置是否允许覆盖注册、获取全部配置源、将配置源加载到上下文、通知监听器Context加载完成。

获取ConfigurableListableBeanFactory并注册单例对象,注册的单例对象包含:ApplicationArguments和Banner。当BeanFactory为DefaultListableBeanFactory时,进入设置是否允许覆盖注册的处理逻辑。当ApplicationArguments类单例对象注册之后,也就意味着我们在使用Spring应用上下文的过程中可以通过依赖注入来使用该对象。

8.2.1、获取全部配置源

public Set<Object> getAllSources() {
		Set<Object> allSources = new LinkedHashSet<>();
    	// 如果primarySources不为空,则将其添加到Set集合中
    	// primarySources为被@SpringBootApplication注解或@EnableAutoConfiguration注解修饰的类
		if (!CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources);
		}
    	// 如果sources不为空,则将其添加到Set集合中
		if (!CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);
		}
		return Collections.unmodifiableSet(allSources);
	}

8.2.2、加载配置源

在获取到所有配置源之后,通过load方法将配置源信息加载到上下文中。

	protected void load(ApplicationContext context, Object[] sources) {
    	// 日志打印
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
    	// 创建BeanDefinitionLoader,通过BeanDefinitionLoader来完成配置资源的加载操作
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
    	// 调用BeanDefinitionLoader的load方法
		loader.load();
	}

	// BeanDefinitionLoader构造方法
	// 可以看出BeanDefinitionLoader支持基于AnnotatedBeanDefinitionReader、XmlBeanDefinitionReader、GroovyBeanDefinitionReader等多种类型的加载操作。
	BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
	}

	// BeanDefinitionLoader中的load方法,加载支持的范围包括:Class、Resource、Package、CharSequence
	private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		if (source instanceof Package) {
			return load((Package) source);
		}
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}

9、刷新应用上下文

应用上下文准备完成之后,便开始对应用上下文进行刷新。

	private void refreshContext(ConfigurableApplicationContext context) {
		// 调用refresh方法
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				// 注册shutdownHook线程, 实现销毁时的回调
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		// 调用spring中的refresh方法, 在refresh中实现对@EnableAutoConfiguration注解的自动配置
		((AbstractApplicationContext) applicationContext).refresh();
	}

最后是通过Spring中的AbstractApplicationContext类的refresh进行刷新,这就属于Spring的内容了,可以简单看下。

public void refresh() throws BeansException, IllegalStateException {
  	// 整个通过同步处理
		synchronized (this.startupShutdownMonitor) {
			// 准备刷新工作
			prepareRefresh();

			// 通知子类刷新并获取BeanFactory, 这个获取到的是DefaultListableBeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 为当前上下文Context准备bean工厂
			prepareBeanFactory(beanFactory);

			try {
				// 设置BeanFactory的后置处理器
				postProcessBeanFactory(beanFactory);

				// 调用BeanFactory的后置处理器, 这些后置处理器是在Bean定义中向容器注册的
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册Bean的后置处理器, 在Bean创建过程中调用
				registerBeanPostProcessors(beanFactory);

				// 对上下文的消息源进行初始化
				initMessageSource();

				// 初始化上下文中的事件机制
				initApplicationEventMulticaster();

				// 初始化其他的特殊Bean
				onRefresh();

				// 检查监听Bean并且将这些Bean向容器注册
				registerListeners();

				// 实例化所有非懒加载的单例Bean
				finishBeanFactoryInitialization(beanFactory);

				// 发布容器事件, 结束刷新过程
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				destroyBeans();

				cancelRefresh(ex);

				throw ex;
			}

			finally {
				resetCommonCaches();
			}
		}
	}

10、调用ApplicationRunner和CommandLineRunner

应用上下文刷新完成之后,调用ApplicationRunner和CommandLineRunner的运行方法, 用来给用户实现一些在容器启动时的自定义操作。

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
    	// 从应用上下文context中获得类型为ApplicationRunner的Bean,并将其加入到集合中
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    	// 从应用上下文context中获得类型为CommandLineRunner的Bean,并将其加入到集合中
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    	// 排序
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
        		// 调用callRunner方法
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

	private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
      		// 执行其run方法 
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值