SpringApplication.run()源码解析1

小龙 388 2022-03-15

准备运行时环境

new SpringApplication() 完成后,下面开始执行run方法:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return new SpringApplication(primarySources).run(args);
}

下图对 run 方法执行流程做了一个大致的梳理

image.png

4. run():启动SpringApplication

先来看前置准备和运行时环境的准备。

	public ConfigurableApplicationContext run(String... args) {
		// 4.1 创建StopWatch对象,该组件是用来监控启动时间的,仅用于验证性能,不重要
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 4.2 创建空的IOC容器,和一组异常报告器
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 4.3 配置与awt相关的信息,设置应用在启动时,即使没有检测到显示器也允许其继续启动
		configureHeadlessProperty();
		// 4.4 获取SpringApplicationRunListeners,并调用starting方法(回调机制)
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
		listeners.starting();
		try {
			// 将main方法的args参数封装到一个对象中
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 4.5 准备运行时环境
			/**
			 * Environment: 它是IOC容器的运行环境,它包括Profile和Properties两大部分,
			 * 		它可以由一个到几个激活的Profile共同配置,它的配置可在应用级Bean中获取。
			 *
			 * ConfigurableEnvironment: 大多数(如果不是全部)Environment 类型的类都将实现的配置接口。
			 * 		提供用于设置 Profile 和默认配置文件以及操纵基础属性源的工具。
			 * 		允许客户端通过ConfigurablePropertyResolver 根接口设置和验证所需的属性、自定义转换服务以及其他功能。
			 */
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
    	}

4.4 getRunListeners:获取SpringApplicationRunListeners

rivate SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
		// 调 getSpringFactoriesInstances 方法,取 spring.factories 中所有 SpringApplicationRunListener
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

继续看一下 SpringApplicationRunListener 源码

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已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners。*/
	default void started(ConfigurableApplicationContext context) {
	}
	/** 在运行方法彻底完成之前立即调用,刷新ApplicationContext并调用所有CommandLineRunners和ApplicationRunner。*/
	default void running(ConfigurableApplicationContext context) {
	}
	/** 当运行应用程序时发生故障时调用。*/
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}
}

4.5 prepareEnvironment 准备运行时环境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 4.5.1 创建运行时环境,默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 4.5.2 配置运行时环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 【回调】SpringApplicationRunListener的environmentPrepared方法(Environment构建完成,但在创建ApplicationContext之前)
		listeners.environmentPrepared(environment);
		// 4.5.3 环境与应用绑定
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

4.5.1 getOrCreateEnvironment:创建运行时环境

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		// 判断当前Web应用类型
		switch (this.webApplicationType) {
			case SERVLET:
				return new StandardServletEnvironment();
			case REACTIVE:
				return new StandardReactiveWebEnvironment();
			default:
				return new StandardEnvironment();
		}
	}

根据当前的应用运行环境类型,创建不同的 Environment 。默认 SpringBoot 环境下会创建 StandardServletEnvironment 。

IOC容器的运行环境,它包括Profile和Properties两大部分,它可由一个到几个激活的Profile共同配置,它的配置可在应用级Bean中获取。

image-1657161713005

4.5.2 configureEnvironment:配置运行时环境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		if (this.addConversionService) {
			/**
			 * ConversionService: 默认 SpringBoot 环境下会创建 StandardServletEnvironment 。
			 * 这个接口的子类 "DefaultConversionService" 实际上就是它在 SpringWebMvc 中做参数类型转换。
			 */
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}

4.5.3 bindToSpringApplication:环境与应用绑定

protected void bindToSpringApplication(ConfigurableEnvironment environment) {
		try {
			/**
			 * Binder: 使用此绑定器的属性源,绑定指定的 可绑定的目标。
			 * 说白了,也就是把配置内容绑定到指定的属性配置类中(类似于 @ConfigurationProperties)。
			 */
			Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
		} catch (Exception ex) {
			throw new IllegalStateException("Cannot bind to SpringApplication", ex);
		}
	}

小结

  • SpringApplication 应用中可以使用 SpringApplicationRunListener 来监听 SpringBoot 应用的启动过程。

  • 在创建IOC容器前,SpringApplication会准备运行时环境 Environment 。

IOC:创建、初始化IOC容器

run:启动SpringApplication

public ConfigurableApplicationContext run(String... args) {
    // ...
    try {
        // ...
        // 4.6 如果有配置 spring.beaninfo.ignore,则将该配置设置进系统参数
        configureIgnoreBeanInfo(environment);
        // 4.7 打印SpringBoot的banner
        Banner printedBanner = printBanner(environment);
        // 4.8 创建ApplicationContext
        context = createApplicationContext();
        // 初始化异常报告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 4.9 初始化IOC容器
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // ...
}

4.6 configureIgnoreBeanInfo:设置系统参数

public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";

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

这里有一个配置:spring.beaninfo.ignore,它的作用是控制是否跳过 BeanInfo 类的搜索

4.7 printBanner:打印Banner

Banner 是一个接口并且内置了一个枚举类型,代表 Banner 输出的模式(关闭、控制台打印、日志输出)。

首先它要看你有没有显式的在 application.properties 中配置 spring.banner.location 这个属性,如果有,就加载它,否则加载默认的位置,叫 banner.txt。

这里体现出了 SpringBoot 的设计原则:约定大于配置

4.8 createApplicationContext:创建IOC容器

// 默认环境 None 
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
		try {
			for (ApplicationContextFactory candidate : SpringFactoriesLoader
					.loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
				ConfigurableApplicationContext context = candidate.create(webApplicationType);
				if (context != null) {
					return context;
				}
			}
			return new AnnotationConfigApplicationContext();
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	};
// REACTIVE 环境
static class Factory implements ApplicationContextFactory {
        Factory() {
        }

        public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
            return webApplicationType != WebApplicationType.REACTIVE ? null : new AnnotationConfigReactiveWebServerApplicationContext();
        }
    }
// SERVLET 环境
static class Factory implements ApplicationContextFactory {
        Factory() {
        }

        public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
            return webApplicationType != WebApplicationType.SERVLET ? null : new AnnotationConfigServletWebServerApplicationContext();
        }
    }

可以发现都是创建的基于Annotation的 ApplicationContext。

创建对应的 ApplicationContext 时就已经在父类 GenericApplicationContext 中创建好了 IOC 容器

public GenericApplicationContext() {
		this.beanFactory = new DefaultListableBeanFactory();
}
  • Servlet - StandardServletEnvironment - AnnotationConfigServletWebServerApplicationContext
  • Reactive - StandardReactiveWebEnvironment - AnnotationConfigReactiveWebServerApplicationContext
  • None - StandardEnvironment - AnnotationConfigApplicationContext

4.9 prepareContext:初始化IOC容器

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将创建好的应用环境设置到IOC容器中
    context.setEnvironment(environment);
    // 4.9.1 IOC容器的后置处理
    postProcessApplicationContext(context);
    // 4.9.2 执行Initializer
    applyInitializers(context);
    // 【回调】SpringApplicationRunListeners的contextPrepared方法(在创建和准备ApplicationContext之后,但在加载之前)
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 创建两个组件:在控制台打印Banner的,之前把main方法中参数封装成对象的组件
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    // 4.9.3 加载主启动类
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 4.9.4 注册主启动类
    load(context, sources.toArray(new Object[0]));
    // 【回调】SpringApplicationRunListeners的contextLoaded方法(ApplicationContext已加载但在刷新之前)
    listeners.contextLoaded(context);
}

这一步中会:注册BeanName生成器、设置资源加载器和类加载器、设置类型转换器、执行 Initializer 、加载 primarySources 和 sources 、load 注册主启动类