Spring Boot源码学习(二) SpringBoot启动流程源码分析一

目录

Spring Boot源码学习(二) SpringBoot启动流程源码分析

一、概述

二、源码分析

1.run()方法总览

2.获取并启动监听器

(1)获取监听器

(2)启动监听器

3.构造容器环境


参考


一、概述

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域成为领导者。本文分析版本为Spring Boot 2.2.X

SpringBoot所具备的特征有:

(1)可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;

(2)内嵌Tomcat或Jetty等Servlet容器;

(3)提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;

(4)尽可能自动配置Spring容器;

(5)提供准备好的特性,如指标、健康检查和外部化配置;

(6)绝对没有代码生成,不需要XML配置。

 

二、源码分析

@SpringBootApplication
public class TestApplication {
	public static void main(String[] args) {
		SpringApplication.run(IcostMsBidApplication.class, args);
	}
}

1.run()方法总览

查看run()方法

  1. 获取并启动监听器
  2. 构造容器环境
  3. 创建容器
  4. 实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  5. 准备容器
  6. 刷新容器
  7. 刷新容器后的扩展接口
public ConfigurableApplicationContext run(String... args) {
                //时间监控 创建并启动计时监控类
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
                //初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
                //设置系统属性
		configureHeadlessProperty();
                //获取spring.factories中的监听器变量,args为指定的参数数组,默认为当前类SpringApplication
		//第一步:获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
                        //初始化默认应用参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                        //第二步:构造容器环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                        //设置需要忽略的bean
			configureIgnoreBeanInfo(environment);
                        //打印banner
			Banner printedBanner = printBanner(environment);
                        //第三步:创建容器 创建应用上下文
			context = createApplicationContext();
                        //第四步:准备异常报告器 实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
			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);
                        //执行所有 Runner 运行器
			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.获取并启动监听器

(1)获取监听器

SpringApplicationRunListeners listeners = getRunListeners(args);

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

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

    
        private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
                //防止重名
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
                //整个 springBoot 框架中获取factories的方式统一如下
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

        //整个 springBoot 框架中获取factories的方式统一如下
        @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;
	}

args本身默认为空,但是在获取监听器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)将当前对象作为参数

(2)启动监听器

listeners.starting() .获取的监听器为EventPublishingRunListener,从名字可以看出是启动事件发布监听器,主要用来发布启动事件

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

EventPublishingRunListener这个是springBoot框架中最早执行的监听器,在该监听器执行started()方法时,会继续发布事件,也就是事件传递。这种实现主要还是基于spring的事件机制。

@Override
	public void starting() {
                //关键代码,这里是创建application启动事件ApplicationStartingEvent
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

因为我们的事件类型为ApplicationEvent,所以会执行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法。大同小异,别的监听器执行流程这里不再赘述,后面会有单独的详解。这里选了一个 springBoot 的日志监听器来进行讲解,核心代码如下:

@Override
	public void onApplicationEvent(ApplicationEvent event) {
                //Springboot启动的时候
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
                //Springboot的Environment环境准备完成的时候
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
                //在Springboot容器的环境设置完成以后
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
                //容器关闭的时候
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
                //容器启动失败的时候
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

3.构造容器环境

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
                // 获取对应的ConfigurableEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
                //配置
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
                //发布环境已准备事件,这是第二次发布事件
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

继续跟踪getOrCreateEnvironment()方法,environment已经被设置了servlet类型,所以这里创建的是环境对象是StandardServletEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

Environment接口提供了4种实现方式

  1. StandardEnvironment普通程序
  2. StandardServletEnvironmentWeb程序
  3. MockEnvironment测试程序的环境
  4. StandardReactiveWebEnvironment响应式web环境

枚举类WebApplicationType是新增的特性

public enum WebApplicationType {

	/**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
        //不需要再web容器的环境下运行,普通项目
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
        //基于servlet的web项目
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.
	 */
        //这个是spring5版本开始的新特性
	REACTIVE;
}

执行到这里,系统变量和环境变量已经被载入到配置文件的集合中,接下来就行解析项目中的配置文件.主要来看一下ConfigFileApplicationListener,该监听器非常核心,主要用来处理项目配置。项目中的 properties 和yml文件都是其内部类所加载

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}

	List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
	}

首先还是会去读spring.factories 文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();

获取的处理类有以下四种:

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

执行完监听器流程后,ConfigFileApplicationListener会执行该类本身的逻辑。由其内部类Loader加载项目制定路径下的配置文件:

// Note the order is from least to most specific (last one wins)
	private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";