SpringBoot源码学习系列——运行流程分析

news2025/1/12 16:13:29

前文SpringBoot源码学习系列——构造流程分析对SpringApplication的实例化流程进行了介绍,完成了基本配置文件的加载及初始化,本文对其run方法进行介绍,看看Spring Boot是如何启动运行的。

run方法核心流程

在这里插入图片描述
可以看到,SpringApplication#run方法返回值为ConfigurableApplicationContext类型的对象,ConfigurableApplicationContext接口继承了ApplicationContext接口。

	public ConfigurableApplicationContext run(String... args) {
	    // 创建StopWatch对象,统计run方法启动时长
	    // 通过StopWatch#start和StopWatch#stop可以便捷的统计代码块运行时长
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 定义容器及异常信息集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 设置java.awt.headless属性,默认为true
		// Headless模式是系统的一种配置模式。在系统可能缺少显示设备、
		// 键盘或鼠标这些外设的情况下可以使用该模式。
		// 当java程序本身不需要显示awt界面,例如命令行程序,后端程序时使用
		// 提高效率、兼容性
		configureHeadlessProperty();
		// 获取SpringApplicationRunListener数组,封装于SpringApplicationRunListeners的listeners属性中
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 遍历listeners 中每个元素SpringApplicationRunListener,启动监听器
		listeners.starting();
		try {
		    // 创建默认的ApplicationArguments对象,封装了参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 加载属性配置
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			// 打印Banner 
			Banner printedBanner = printBanner(environment);
			// 创建ApplicationContext容器
			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);
			}
			// 通知监听器,容器started
			listeners.started(context);
			// 调用ApplicationRunner和CommandLineRunner的run方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		    // 通知监听器,容器running
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

此节我们对大致流程进行简要描述,下文对各步骤进行详细分析。

SpringApplicationRunListener监听器

监听器的配置及加载流程

在这里插入图片描述
如下为SpringApplicationRunListeners的源码,存在一个成员变量listeners,并提供了一系列针对listenersSpringApplicationRunListener监听器进行遍历处理的方法。
在这里插入图片描述
getRunListeners方法

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 通过SpringApplicationRunListeners的构造方法完成listeners加载
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

getSpringFactoriesInstances方法

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 通过SpringFactoriesLoader.loadFactoryNames获取
		// spring.factories文件中的SpringApplicationRunListener属性值
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 实例化监听器
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

在这里插入图片描述

createSpringFactoriesInstances方法

	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<?> instanceClass = ClassUtils.forName(name, classLoader);
				// 检查类型
				Assert.isAssignable(type, instanceClass);
				// 获取有参构造器,参数为Class<?>[] parameterTypes
				// 参数数组内容为Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
				// 因此,SpringApplicationRunListener的实现类应有默认构造方法,
				// 参数依次为SpringApplication.class, String[].class
				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;
	}

SpringApplicationRunListener源码解析

如下,可以看到run方法各个阶段的时间监听处理方法

public interface SpringApplicationRunListener {

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 * 执行run方法,立即调用
	 */
	default void starting() {
	}

	/**
	 * Called once the environment has been prepared, but before the
	 * {@link ApplicationContext} has been created.
	 * @param environment the environment
	 * environment准备完成,ApplicationContext创建前
	 */
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	/**
	 * Called once the {@link ApplicationContext} has been created and prepared, but
	 * before sources have been loaded.
	 * @param context the application context
	 * ApplicationContext构建完成,资源加载前
	 */
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * @param context the application context
	 * ApplicationContext加载完成,刷新前
	 */
	default void contextLoaded(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
	 * ApplicationContext刷新启动完成,CommandLineRunner和ApplicationRunner执行前
	 */
	default void started(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
	 * 所有准备工作就绪,run方法执行完成前
	 */
	default void running(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
	 * 程序报错
	 */
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

EventPublishingRunListener类

EventPublishingRunListener是SpringBoot中SpringApplicationRunListener的唯一实现类。
如下,程序执行到某些步骤,调用EventPublishingRunListener 的某方法,封装生成事件对象,然后通过initialMulticastermulticastEvent方法或ConfigurableApplicationContext publishEvent方法完成事件发布。

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;
    
    // 构造方法中进行参数初始化,创建了SimpleApplicationEventMulticaster事件广播器
    // 并将监听器依次添加进事件广播器
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		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
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

    // 该步中完成context的初始化,该步骤之后的方法都通过context的publishEvent进行事件发布
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			// 将监听器添加到上下文context中
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	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
	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 final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

初始化ApplicationArguments

监听器启动后,进行ApplicationArguments对象初始化,ApplicationArguments对象是mian方法中的参数的封装,将参数封装成了Source对象。

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;
	}

初始化ConfigurableEnvironment

通过DefaultApplicationArguments完成ApplicationArguments的初始化后,进行ConfigurableEnvironment的初始化:
在这里插入图片描述
首先看一下ConfigurableEnvironment,该接口继承自EnvironmentConfigurablePropertyResolver接口,均继承自PropertyResolverConfigurableEnvironment接口提供了当前运行环境的公开接口,如配置文件Profiles中系统属性和变量的设置、添加、读取、合并等功能接口。
在这里插入图片描述
下面简单看下prepareEnvironment方法,下文对各步骤进行详细说明:

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 获取或创建环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		// 配置listener环境
		listeners.environmentPrepared(environment);
		// 绑定环境到SpringApplication
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

获取或创建环境

	private ConfigurableEnvironment getOrCreateEnvironment() {
	    // 环境不为null,直接返回
		if (this.environment != null) {
			return this.environment;
		}
		// 环境为null,根据SpringApplication初始化阶段推断的webApplicationType创建对应环境
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

配置环境

	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
	    判断是否设置数据转换服务
		if (this.addConversionService) {
			ConversionService conversionService = ApplicationConversionService.getSharedInstance();
			environment.setConversionService((ConfigurableConversionService) conversionService);
		}
		// 配置PropertySources
		configurePropertySources(environment, args);
		// 配置Profiles
		configureProfiles(environment, args);
	}

下面进行逐步解析:

	protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
	    // 获取属性资源信息,以StandardServletEnvironment为例,属性资源如下:
	    // new StubPropertySource("servletConfigInitParams")
	    // new StubPropertySource("servletContextInitParams")
	    // new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())
	    // new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())
		MutablePropertySources sources = environment.getPropertySources();
		// 存在默认属性资源,则添加到PropertySources最后
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
			sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
		}
		// 存在命令行属性
		if (this.addCommandLineProperties && args.length > 0) {
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			// 默认属性资源包含命令行属性,通过CompositePropertySource进行组合
			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));
			}
		}
	}

完成上面所述PropertySources配置后,进行Profiles配置:

	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
	    // 设置哪些配置文件处于激活状态,对应spring.profiles.active的属性值
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}

忽略信息配置

获取环境中spring.beaninfo.ignore的属性值,设置到系统参数中,默认true,该配置项用于决定是否跳过BeanInfo类的扫描。true表示不需要jdk去缓存BeanInfo信息,spring自己缓存。spring在创建bean的时候会利用jdk的一些工具来解析一个类的相关信息,jdk在解析一个类的信息的时候会进行缓存,这里就是禁止了jdk的缓存。

	private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
		if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
		}
	}

到此就完成了Environment的基本处理。

打印banner

默认使用SpringBootBanner

	private Banner printBanner(ConfigurableEnvironment environment) {
	    // 判断Banner打印开关
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader());
		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);
	}

Spring应用上下文创建

在这里插入图片描述
下面我们分析下createApplicationContext方法,该方法完成Spring应用上下文的创建

	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);
			}
		}
		// 通过Spring提供的BeanUtils进行实例化
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

可以看到,该方法中通过枚举类型进行判断,根据前面推断得到的web应用类型创建不同的上下文容器。

Spring应用上下文准备

在这里插入图片描述
下面首先看下prepareContext方法的源码吗,以listeners.contextPrepared(context);为界,以上为准备阶段,以下为加载阶段:

    // 参数为前面创建的上下文对象context、环境environment、监听器listener、封装的参数对象applicationArguments、banner对象
	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	    // 设置上下文的配置环境
		context.setEnvironment(environment);
		// 应用上下文后置处理
		postProcessApplicationContext(context);
		// 初始化上下文
		applyInitializers(context);
		// 通知监听器context准备完成,以上为上下文准备,以下为上下文加载
		listeners.contextPrepared(context);
		// 打印日志,启动profile
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		// 获得ConfigurableListableBeanFactory,注册单例对象
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
		    // 注册printedBanner对象
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
		    // 设置是否允许覆盖注册
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// 添加懒加载后置处理器
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		// 获取配置源,添加到context
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 通知监听器context加载完成
		listeners.contextLoaded(context);
	}

应用上下文准备阶段

从上面源码可知,准备阶段包含三步:设置环境environment,后置处理器和ApplicationContextInitializer初始化上下文。

  • 设置环境environment
	@Override
	public void setEnvironment(ConfigurableEnvironment environment) {
	    // 设置environment
		super.setEnvironment(environment);
		// 设置environment的reader和scanner属性的conditionEvaluator属性
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}
  • 后置处理
	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
	    // 如果beanNameGenerator不为null,按默认名org.springframework.context.annotation.internalConfigurationBeanNameGenerator注册单例对象,bean名称生成策略
		if (this.beanNameGenerator != null) {
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		// resourceLoader不为空,根据不同类型进行设置
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		// 设置转换服务
		if (this.addConversionService) {
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}
  • applyInitializers进行上下文初始化
	protected void applyInitializers(ConfigurableApplicationContext context) {
	    // 获取ApplicationContextInitializer集合,遍历进行初始化context
	    // 此处getInitializers对initializers的值进行了排序去重,initializers即前面SpringApplication初始化阶段设置的值
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
		    // 判断initializer 和 context 类型是否匹配
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			// 初始化context
			initializer.initialize(context);
		}
	}

应用上下文加载阶段

上下文加载阶段包含以下几步:日志打印及profile设置、设置是否允许覆盖注册、获取配置源、加载配置源到context、通知监听器context加载完成。

	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);
		}
		// Add boot specific singleton beans
		// 获取ConfigurableListableBeanFactory 
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 注册applicationArguments单例对象
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		// 注册printedBanner单例对象
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		// 设置覆盖注册,即是否允许名称相同的bean的覆盖,springboot中默认false
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// 开启了懒加载,则需设置后置处理器
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		// 获取配置源集合
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 将配置源信息加载到context
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}
	public Set<Object> getAllSources() {
		Set<Object> allSources = new LinkedHashSet<>();
		// 设置primarySources,primarySources是SpringApplication的构造函数参数,一般为主类
		if (!CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources);
		}
		// // 设置sources,通过setResources方法设置的值
		if (!CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);
		}
		// 返回不可修改的集合
		return Collections.unmodifiableSet(allSources);
	}
protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// 通过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);
		}
		loader.load();
	}

loader.load()

	int load() {
		int count = 0;
		for (Object source : this.sources) {
			count += load(source);
		}
		return count;
	}
	
	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());
	}

从上面load方法可以看出,BeanDefinitionLoader加载支持的范围包括Class、Resource、Package、CharSequence,sources来源包括primarySources和setResources两种,分别对应Class和CharSequence。

Spring应用上下文刷新

	private void refreshContext(ConfigurableApplicationContext context) {
	    // 核心方法
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
	@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.
			// 通知子类刷新内部bean factory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// 为context准备bean factory
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 运行context的子类第bean factory进行后置处理
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 调用context中注册为bean的工厂处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册bean处理器
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 初始化信息源
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化事件广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 初始化其他子类特殊的bean
				onRefresh();

				// Check for listener beans and register them.
				// 检查并注册事件监听器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 实例化非懒加载的单例bean
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 发布事件,该方法之后,spring应用上下文正式开启
				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();
			}
		}
	}

refreshContext完成后,Spring应用上下文完成启动,继续执行afterRefresh,其默认实现为空。

调用AppllicationRunner和CommandLineRunner

下面继续看下callRunners方法:

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
	    // 将ApplicationRunner和CommandLineRunner类型的bean添加到runner列表中,并排序(@Order注解或者实现Ordered接口控制顺序)
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		// 遍历runner依次调用callRunner方法
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

通过实现ApplicationRunnerCommandLineRunner接口并重写run()方法,实现应用初始化时的一些自定义操作执行,如初始化某参数或组件等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/436140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

主机串口—虚拟机串口—linux系统串口之间的关系(实现主机和虚拟机之间的串口通信)

目录 1、准备工具 2、实现机理 3、实现过程 4、虚拟机串口 —— Linux系统串口文件 1、准备工具 VSPD&#xff1a;作用是在主机上创建虚拟串口 VSPD 下载&#xff1a;VSDP 汉化版下载VSPD 配置教程&#xff1a;VSPD虚拟串口工具实用工具–小白入门篇 | 码农家园 串口调…

MES功能设计规格书

软件功能结构 项目实施方案 概述 按总体规划&#xff0c;MES项目分三个阶段实施&#xff0c;第一阶段先在终端电器制造部和控制电器制造部进行试点实施。 第一阶段目标 建立基本的MES系统框架&#xff0c;提供工厂建模等基础功能。将目前的Excel生产排程纳入到系统管理&…

wps宏编辑器API关于msgbox和inputbox的使用说明

WPS宏编辑器API参考关于函数列举了3个&#xff1a;DoEvents、InputBox和MsgBox&#xff0c;其中DoEvents有点不好理解&#xff0c;应该在什么场景下使用也缺乏官网指导说明&#xff0c;因此本文重点讲述InputBox和MsgBox的使用说明。 1、DoEvents 处理进程的消息队列中的消息…

AI菜鸡浅谈ChatGpt

最近最火的话题可能就是Chatgpt &#xff0c;这个对话机器人横空出世&#xff0c;大大突破了人类之前对AI 对话机器人的认知和预期&#xff0c;上次这样的颠覆认知的突破还是7年前的阿法狗&#xff0c;但是这一次Chatgpt 带来的革命可能要更深远。我从以下几个方面来谈一下我对…

Centos7.6集群部署海豚调度3.1.5

目录 前置准备工作&#xff08;所有机器&#xff09;主机规划数据库规划用户规划目录规划配置/etc/hostsjdk安装进程树分析配置ssh免密部署zookeeper启动zookeeper下载DolphinScheduler 二进制包修改install_env.sh配置修改dolphinscheduler_env.sh配置文件 安装&#xff08;ty…

virtualbox 安装centos

在virtualbox安装centos时&#xff0c;遇到了一些问题&#xff0c;此处记录下&#xff0c;希望可以帮助一些小伙伴。 一、下载centos 进入官网下载地址&#xff1a;Download (centos.org) 然后选择阿里云镜像地址&#xff1a;centos-7.9.2009-isos-x86_64安装包下载_开源镜像…

TensorFlow版本与其他库的版本之间问题

使用TensorFlow的版本不一样&#xff0c;对应的库的版本也需不一样&#xff0c;这个有许多需要注意的地方。 比如Keras库&#xff0c; 当我使用tensorflow2.1.0版本时&#xff0c;安装Keras2.10.0这个库会导致运行报错&#xff0c; 那么就需要降低其版本到与之匹配&#xff…

AQS 和CAS详解

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;JDBC事务 Hibernate事务 EJB事务详解 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你…

初识Linux篇

初识Linux篇 Linux环境的搭建1.什么是Linux2.Linux环境的安装云服务器Linux安装 总结 Linux环境的搭建 &#x1f60a;首先&#xff0c;在学习Linux之前&#xff0c;先了解一下Linux&#x1f60a; 1.什么是Linux Linux是一种操作系统软件&#xff0c;就像手机中的安卓&#xff…

Vue中的导航守卫

router官网-导航守卫 导航守卫常用的主要有三种&#xff1a;全局前置守卫(beforeEach)、路由独享守卫(beforeEnter)、组件内守卫(beforeRouteEnter) 路由独享守卫 在路由配置上直接定义 beforeEnter 守卫 <body> <div id"app"><h1>欢迎使用路由&l…

在没有魔法的情况下,如果让ChatGPT帮我们写代码

ChatGPT写代码 ✋ChatGPT 是一个基于人工智能的自然语言处理模型&#xff0c;可以帮助程序员更高效地、更自然地与计算机交互。ChatGPT 可以解决程序员在日常开发中遇到的各种问题&#xff0c;例如语法错误、API 使用、代码实现、架构设计等等。 &#x1f4a5;通过与 ChatGPT…

操作系统1(什么是操作系统、程序和编译器)

1.什么是操作系统&#xff1f; 1.什么是操作系统&#xff1f; 对单一计算机硬件系统做出抽象、支撑程序执行的软件系统。通过“虚拟化”硬件资源为程序运行提供服务的软件。 操作系统可以访问任何硬件资源。 2.什么是程序&#xff1f; 程序就是一个状态机。 程序计算sysc…

面了十几家公司测试岗,我终于悟了,面试无非就是这些题

测试岗的面试其实都是大同小异的&#xff0c;这里我收集整理了185道高频面试题&#xff0c;希望对在找工作或者准备跳槽的各位小伙伴有所帮助&#xff01; 一. 测试基础 1.如何制定测试计划 参考答案&#xff1a; 测试计划包括测试目标、测试范围、测试环境的说明、测试类型…

SpringBoot 实现多个子域共享 cookie

SpringBoot 实现多个子域共享 cookie 项目信息cookie 共享需求如何实现 环境配置配置域SpringBoot 配置 https 访问 后端代码验证验证后端解析 cookie 项目信息 使用SpringBoot web框架&#xff0c;版本号 2.7.10 <dependency><groupId>org.springframework.boot&…

Ambari 操作HDP组件迁移

目录 ​ 一、集群信息 1.1 HDP版本信息 1.2 服务器信息 二、服务迁移操作 一、集群信息 1.1 HDP版本信息 1.2 服务器信息 角色 IP 组件 hdp103 192.168.2.152 NameNode hdp104 192.168.2.153 新 NameNode hdp105 192.168.2.154 旧NameNode 二、服务迁移操作 我…

6、苹果签名原理

一、iOS应用签名原理 代码签名双层代码签名描述文件 1.1 代码签名 代码签名是对可执行文件或脚本进行数字签名,用来确认软件在签名后未被修改或损坏的措施. 和数字签名原理一样,只不过签名的数据是代码. 1.1.1 简单的代码签名 - 在iOS出来之前,以前的主流操作系统(Mac/Win…

自动控制原理模拟卷5

自动控制原理模拟题五 Question1 液位自动控制系统原理示意图如下图所示,在任意情况下,希望液面高度 c c c维持不变,说明系统工作原理并画出系统方块图。 解: 当电位器电刷位于中点位置时,电动机不动,控制阀门有一定的开度,使水箱中流入水量与流出水量相等,从而液面保…

C6678-缓存和内存

C6678-缓存和内存 全局内存映射扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展使用例程缓存 全局内存映射 扩展内存控制器&#xff08;XMC&#xff09;-MPAX内存保护与地址扩展 每个C66x核心都具有相同大小的L1和L2缓存&#xff0c;并且可配置为普通内存使…

【微信小程序-原生开发】实用教程21 - 分包

分包的流程 当微信小程序主包大小超过2M时&#xff0c;则需要对微信小程序进行分包&#xff0c;方法如下&#xff1a; 1. 转移页面文件 在项目根目录下&#xff0c;新建文件夹 package1 &#xff08;即自定义的分包名为 package1 &#xff09;文件夹 package1 内新建文件夹 p…

C嘎嘎~~【初识C++ 中篇】

初识C 中篇 1.缺省参数2.函数重载2.1函数重载的概念2.2函数重载的原理 3.auto关键字3.1类型别名思考&#xff08;typedef&#xff09;3.2auto的简介3.3auto的使用规则3.4 auto不能推导的场景 4.基于范围的for循环4.1范围for的语法4.2范围for的使用条件 1.缺省参数 缺省参数是 声…