Spring 系列目录(http://www.javashuo.com/article/p-kqecupyl-bm.html)html
@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } }
本节重点分析 Spring Boot(2.1.3) 的 SpringApplication#run 方法是如何启动 Spring 容器。run 方法最终调用 new SpringApplication(primarySources).run(args)
。java
// 1. 配置类,primarySources 是 run 方法传入的 private Set<Class<?>> primarySources; private Set<String> sources = new LinkedHashSet<>(); // 2. main 方法所在的启动类,日志输出用 private Class<?> mainApplicationClass; // 3.environment 环境配置相关,addCommandLineProperties 添加 main 方法的命令行参数到 environment private boolean addCommandLineProperties = true; private boolean addConversionService = true; private Map<String, Object> defaultProperties; private Set<String> additionalProfiles = new HashSet<>(); private boolean isCustomEnvironment = false; // 4. webmvc、webflux、非web 对应的 ApplicationContext 不一样 private Class<? extends ConfigurableApplicationContext> applicationContextClass; private WebApplicationType webApplicationType; // 5. 经过 spring.factories 配置 private List<ApplicationContextInitializer<?>> initializers; private List<ApplicationListener<?>> listeners;
public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // 1. primarySources 为配置类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 2. 根据加载的 jar 推断是 web、webflux、非web this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 3. 加载 ApplicationContextInitializer 到 initializers 中。 spring.factories setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 4. 加载监听器到 listeners 中。 spring.factories setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 5. mainApplicationClass 启动类,即根据 new RuntimeException().getStackTrace() // 栈信息推断 main 方法所在的类,本例中即为 MyApplication,用于输出日志用 this.mainApplicationClass = deduceMainApplicationClass(); }
在构造方法中重点关注第三步和第四步,经过 Spring 的 SPI 技术(相似 JDK 的 ServiceLoader,在 Spring 中为 SpringFactoriesLoader)向容器中注入在 spring.factories 中配置的组件:web
第三步:注入 ApplicationContextInitializer 的配置类。Spring Boot 默认组装了 6 个实例,spring-boot-2.1.3.RELEASE.jar 下 4 个,spring-boot-autoconfigure-2.1.3.RELEASE.jar 下 2 个。spring
0 = {DelegatingApplicationContextInitializer@1866} 1 = {SharedMetadataReaderFactoryContextInitializer@1867} 2 = {ContextIdApplicationContextInitializer@1868} 3 = {ConfigurationWarningsApplicationContextInitializer@1869} 4 = {ServerPortInfoApplicationContextInitializer@1870} 5 = {ConditionEvaluationReportLoggingListener@1871}
第四步:注入监听器,Spring 是基于事件驱动的,如配置文件的加载。Spring Boot 默认组装了 10 个实例,spring-boot-2.1.3.RELEASE.jar 下 9 个,spring-boot-autoconfigure-2.1.3.RELEASE.jar 下 1 个。mvc
0 = {ConfigFileApplicationListener@1771} 1 = {AnsiOutputApplicationListener@1772} 2 = {LoggingApplicationListener@1773} 3 = {ClasspathLoggingApplicationListener@1774} 4 = {BackgroundPreinitializer@1775} 5 = {DelegatingApplicationListener@1776} 6 = {ParentContextCloserApplicationListener@1777} 7 = {ClearCachesApplicationListener@1778} 8 = {FileEncodingApplicationListener@1779} 9 = {LiquibaseServiceLocatorApplicationListener@1780}
那 Spring 是如何保证这些组件的执行顺序的呢?在 getSpringFactoriesInstances 获取全部的实例后都会进行排序 AnnotationAwareOrderComparator.sort(instances)
。 详见:http://www.javashuo.com/article/p-xjjqneke-dc.htmlapp
run 方法主要是启动 ApplicationContext 容器,省略了一些非必须的代码。dom
public ConfigurableApplicationContext run(String... args) { ConfigurableApplicationContext context = null; // 1. listeners 用户监听容器的运行,默认实现为 EventPublishingRunListener SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 2. 初始化环境变量 environment ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 3. 仅仅实例化对应的 ApplicationContext,尚未任何配置 context = createApplicationContext(); // 4. 配置 context,为刷新容器作好准备 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 5. AbstractApplicationContext#refresh refreshContext(context); afterRefresh(context, applicationArguments); listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { throw new IllegalStateException(ex); } return context; }
getRunListeners
初始化 SpringApplicationRunListeners,也是经过 spring.factories 配置 EventPublishingRunListener。SpringApplicationRunListeners 监听了容器启动、环境准备等事件。ide
prepareEnvironment
初始化环境变量 environment,根据是不是 web 程序启动不一样的 Environment 实现。同时将 ①配置的默认数据源 defaultProperties;②main 方法的参数;③application.properties 配置文件看成 Environment 的数据源。spring-boot
createApplicationContext
实例化 ApplicationContextpost
prepareContext
配置 ApplicationContext
refreshContext
刷新 ApplicationContext
SpringApplicationRunListeners 默认实现为 EventPublishingRunListener。注意之因此不用 ApplicationContext 直接触发事件,是由于只有到第 4 步 contextLoaded 以后容器的初始化工做才完成,此时才能用 context.publishEvent() 触发相应的事件。
public interface SpringApplicationRunListener { // 1. 调用 run 方法后首先触发 starting 事件 void starting(); // 2. prepareEnvironment。初始化 environment 时调用, void environmentPrepared(ConfigurableEnvironment environment); // 3. prepareContext 开始时调用 void contextPrepared(ConfigurableApplicationContext context); // 4. prepareContext 完成时调用 void contextLoaded(ConfigurableApplicationContext context); // 5. refreshContext 后调用 void started(ConfigurableApplicationContext context); // 6. started 后调用,run 方法调用完成 void running(ConfigurableApplicationContext context); void failed(ConfigurableApplicationContext context, Throwable exception); }
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 1. 根据 webApplicationType 建立相应的 Environment ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2. 配置 Environment,主要有三点:一是 ConversionService;二是数据源,包括命令行参数;三是 Profiles configureEnvironment(environment, applicationArguments.getSourceArgs()); // 3. 激活 environmentPrepared 事件,主要是加载 application.yml 等配置文件 // ConfigFileApplicationListener#ApplicationEnvironmentPreparedEvent listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } // ??? 之后再研究 ConfigurationPropertySources.attach(environment); return environment; }
加载后的数据源以下:
0 = {SimpleCommandLinePropertySource@2697} "SimpleCommandLinePropertySource {name='commandLineArgs'}" 1 = {PropertySource$StubPropertySource@2698} "StubPropertySource {name='servletConfigInitParams'}" 2 = {PropertySource$StubPropertySource@2699} "StubPropertySource {name='servletContextInitParams'}" 3 = {MapPropertySource@2700} "MapPropertySource {name='systemProperties'}" 4 = {SystemEnvironmentPropertySourceEnvironmentPostProcessor$OriginAwareSystemEnvironmentPropertySource@2701} "OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}" 5 = {RandomValuePropertySource@2702} "RandomValuePropertySource {name='random'}" 6 = {OriginTrackedMapPropertySource@2703} "OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.properties]'}"
执行加载的过程以下:
没什么可说的,略过
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 1. 为 context 注入基本的组件 context.setEnvironment(environment); postProcessApplicationContext(context); // 2. List<ApplicationContextInitializer<?>> initializers 在初始化的时候已经注入 applyInitializers(context); // 3. 触发 contextPrepared 事件 listeners.contextPrepared(context); // 4. 配置 beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 5. load 方法加载 BeanDefinition Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); // 6. 触发 contextLoaded 事件,此时 context 准备工做已经完成 listeners.contextLoaded(context); }
prepareContext 主要是配置 context 和 beanFactory。其中最重要的方法是 load(context, sources.toArray(new Object[0]))
方法,向容器中加载 BeanDefinition。sources 指的是 Spring 的配置类,默认为 this.primarySources 即 SpringApplication.run(Application.class, args) 中的配置类 Application。
下面主要看一下 load 是如何加载 BeanDefinition 的。
protected void load(ApplicationContext context, Object[] sources) { 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(); } protected BeanDefinitionLoader createBeanDefinitionLoader( BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }
load 将加载 BeanDefinitionLoader 委托给了 BeanDefinitionLoader#load() 方法,其中 sources 即为配置类。
没什么可说的,见 AbstractApplicationContext#refresh(http://www.javashuo.com/article/p-tpkmaktp-ed.html)
天天用心记录一点点。内容也许不重要,但习惯很重要!