Spring Boot启动源码分析

93 阅读9分钟

1、SpringApplication的构造方法

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//设置servlet环境,分为非web环境、web环境、reactive环境三种
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//加载META-INF/spring.factories文件中的ApplicationContextInitializer和ApplicationListener
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//推断main方法所在的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这里主要是通过判断REACTIVE相关的字节码是否存在,如果不存在,则web环境即为SERVLET类型。这里设置好web环境类型,在后面会根据类型初始化对应环境。

static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

deduceMainApplicationClass的实现原理比较巧妙,这里新建了一个运行时异常对象,通过这个对象获取当前的调用函数堆栈数组StackTrace,之后遍历这个堆栈数组,找到方法名为main的类,返回这个类。

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

SpringApplication类初始化完成之后执行run方法开始启动流程,启动流程分为七个步骤:

  • 第一步:获取并启动监听器
  • 第二步:构造容器环境
  • 第三步:创建容器
  • 第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  • 第五步:准备容器
  • 第六步:刷新容器
  • 第七步:刷新容器后的扩展接口
public ConfigurableApplicationContext run(String... args) {
		//时间监控
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
		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);
			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;
	}

一、获取并启动监听器

1、获取监听器

获取监听器的源码核心是下面两个方法,这里是先加载SpringApplicationRunListener,EventPublishingRunListener实现了SpringApplicationRunListener接口,通过反射生成实例时,会触发EventPublishingRunListener的构造方法,构造方法会将spring.factories中的监听器(SpringApplication的构造方法已经将其加载到内存中)传递到SimpleApplicationEventMulticaster中,后续启动监听器,将从这里获取到这些监听器。 除此之外,下面两个方法还用来加载META-INF/spring.factories文件中的ApplicationContextInitializer和ApplicationListener。

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));
		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;
	}
//EventPublishingRunListener的构造方法
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);
		}
	}

2、启动监听器

getRunListeners(args)方法执行时调用了SpringApplicationRunListeners的构造方法,所以starting()方法里的listeners只有EventPublishingRunListener对象

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

EventPublishingRunListener对象的starting()方法会发布事件类型为ApplicationStartingEvent的事件,这里主要是使用spring的事件机制实现事件传递。

@Override
public void starting() {
	this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

下面的代码时事件启动的核心,会先根据事件类型获取到预先加载到AbstractApplicationEventMulticaster的监听器,这里获取到的是事件类型为ApplicationStartingEvent的事件,在运行的不同阶段会执行不同的监听器。

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    //获取线程池,如果为空则同步处理。这里线程池为空,还未没初始化。
    Executor executor = this.getTaskExecutor();
    //根据事件类型获取监听器
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        if (executor != null) {
        //异步发布事件
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
        //同步发布事件
            this.invokeListener(listener, event);
        }
    }

}

二、构造容器环境

构造容器环境的时候首先会得到一个ConfigurableEnvironment对象,getOrCreateEnvironment()方法会根据运行环境返回不同的environment对象,Environment接口提供了4种实现方式,StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment,分别代表普通程序、Web程序、测试程序的环境、响应式web环境,当new一个environment对象时,会完成一些列初始化工作获取到系统变量和环境变量,并将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中。 系统环境初始化完成之后会发布一个事件,与启动监听器类似,完成对项目中配置文件的解析。这里会获取到事件类型为ConfigurableEnvironment的监听器,最为核心的一个监听器是ConfigFileApplicationListener,他主要用来 properties 和yml文件。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 创建ConfigurableEnvironment对象,并且获取运行环境(servlet或reactive或非web环境)的初始化参数和上下文参数,系统变量和环境变量参数
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置动作,将运行机器的系统变量和环境变量,加入到其父类AbstractEnvironment定义的对象MutablePropertySources中
		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;
	}

ConfigFileApplicationListener的onApplicationEnvironmentPreparedEvent会先获取到spring.factories中的EnvironmentPostProcessor环境配置处理器,ConfigFileApplicationListener会先执行所有的EnvironmentPostProcessor,最后执行自生的代码逻辑,利用其内部类Loader加载指定目录下的配置文件。

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

三、创建容器

创建容器的逻辑非常的简单,他是根据webApplicationType判断,创建与普通web环境,webflux环境和非web环境对应的容器,同样是使用的反射来创建对象实例。

/**
 * The class name of application context that will be used by default for web
 * environments.
 */
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
		+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

/**
 * The class name of application context that will be used by default for reactive web
 * environments.
 */
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
		+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

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);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

四、报告错误信息

通过源码可以看出,先调用了getSpringFactoriesInstances()方法获取到spring.factories中的FailureAnalysisReporter,当在启动过程中捕捉到异常时,执行handleRunFailure()方法,

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
			Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
		try {
			try {
				//处理程序退出码
				handleExitCode(context, exception);
				if (listeners != null) {
					//执行监听器的失败方法
					listeners.failed(context, exception);
				}
			}
			finally {
				//上报打印错误信息
				reportFailure(exceptionReporters, exception);
				if (context != null) {
					context.close();
				}
			}
		}
		catch (Exception ex) {
			logger.warn("Unable to close ApplicationContext", ex);
		}
		ReflectionUtils.rethrowRuntimeException(exception);
	}

五、容器准备

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	//设置容器环境,包含各种参数和变量
	context.setEnvironment(environment);
	//容器的后置处理,因为beanNameGenerator和resourceLoader默认为空,所以这个方法只会执行数据类型转化器的注册
	postProcessApplicationContext(context);
	//执行初始化处理器(spring.factories中定义的Application Context Initializers)
	applyInitializers(context);
	//发送容器已经准备好的事件,通知到各种监听器,这里会按照事件类型为ApplicationContextInitializedEvent过滤监听器
	listeners.contextPrepared(context);
	//打印日志
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// Add boot specific singleton beans
	//加载Spring Boot中特殊的两个单例类springApplicationArguments和springBootBanner
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
	//设置beanFactory中是否允许重复bean覆盖
		((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");
	load(context, sources.toArray(new Object[0]));
	//发布容器已经加载完成的事件
	listeners.contextLoaded(context);
}

六、刷新容器

Spring Boot在做容器刷新的时候其实做了两件事情,首先是刷新容器,而且刷新容器的主要逻辑都是由Spring完成的,这也是Spring实现IOC和AOP的关键,完成容器刷新之后,会注册一个程序关闭使用的钩子函数。

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
			/**
			* 这个狗子函数的本质是创建了一个线程,用于在系统退出时关闭资源,平滑退出,以下情况会触发钩子函数:
			* 程序正常退出
		    * 使用System.exit()
		    * 终端使用Ctrl+C触发的中断
		    * 系统关闭
		    * OutofMemory宕机
		    * 使用Kill pid杀死进程(使用kill -9是不会被调用的)
			*/
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			/**
			 * 刷新上下文环境
			 * 初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验
			 * 如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,
			 * 重写initPropertySources方法就好了
			 */
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			//初始化BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			/**
			 * 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
			 * 设置SPEL表达式#{key}的解析器
			 * 设置资源编辑注册器,如PerpertyEditorSupper的支持
			 * 添加ApplicationContextAwareProcessor处理器
			 * 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
			 * 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
			 */
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				//提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				/**
				 * 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
				 * 执行对应的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
				 */
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				/**
				 * 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
				 * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
				 */
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				//初始化上下文中的资源文件
				initMessageSource();

				// Initialize event multicaster for this context.
				//初始化上下文事件广播器,并放入applicatioEventMulticaster
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				//添加实现了ApplicationListener的监听器的bean实例
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				/**
				 * 设置转换器
				 * 注册一个默认的属性值解析器
				 * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
				 * 初始化剩余的非延迟加载的单例bean
				 */
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				/**
				 * 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,
				 * spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如一直轮询kafka
				 * 启动所有实现了Lifecycle接口的类
				 * 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理,即对那种在spring启动后需要处理的一些类,这些类实现了
				 * ApplicationListener<ContextRefreshedEvent> ,这里就是要触发这些类的执行(执行onApplicationEvent方法)另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
				 * 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
				 */
				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();
			}
		}
	}

七、刷新容器后的扩展接口

容器刷新完成之后的后置处理,默认是没有任何操作的,主要用于功能的扩展,可重写该方法自定义实现一些启动完成之后的操作。

/**
 * Called after the context has been refreshed.
 * @param context the application context
 * @param args the application arguments
 */
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}