Spring系列-1 启动流程

背景

从本文开始,开启一个新的专题Spring系列,用于收集Spring框架相关的文章;通过使用方式、案例演示、源码分析等方式对Spring进行介绍。
该系列将包括以下文章:
1.Spring系列—启动流程
2.Spring系列—Bean的生命周期
3.Spring系列—Bean实例化与依赖注入
4.Spring系列—循环依赖与三级缓存
5.Spring系列—事件机制
6.Spring系列—占位符使用和原理
7.Spring系列—国际化
8.Spring系列—AOP原理
9.Spring系列—Async注解使用与原理
10.Spring系列—事务机制

预计每周末更新一篇,预计持续时间3个月左右。

本文介绍Spring启动流程,重点在于容器的刷新过程。

1.Spring启动流程

BeanFactory提供了IOC相关的能力,称为IOC容器;SpringApplication作为BeanFactory的子类,在其基础上提供了事件机制、国际化、资源处理等功能,称为Spring上下文或者Spring容器。
SpringApplication的核心实现在AbstractSpringApplication类中,Spring启动流程也是在该类的refresh()方法中完成。AbstractSpringApplication类在内部维持了一个BeanFactory对象(默认为DefaultListableBeanFactory类型);容器相关的所有功能由该BeanFactory对象提供,而AbstractSpringApplication在此基础上进行了一层代理封装。
相对于BeanFactory的懒加载机制,Spring容器在启动过程中会将所有的非lazy类型的Bean对象加载到IOC容器中。
Spring启动流程可以看成Spring容器组件初始化、向IOC容器注册Bean对象以及对需要AOP的对象完成代理的过程;其中,组件初始化包括IOC容器的初始化、事件组件、国际化组件等。

2.使用方式

如果准备开始阅读源码,第一件事应该写一个demo

2.1 xml 配置文件方式

spring-context.xml 配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         https://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:component-scan base-package="com.seong.xml.componentscan"/>

    <bean id="componentA" class="com.seong.xml.component.ComponentA"/>
</beans>

如上所示:可以通过bean标签进行Bean对象的声明;也可以通过component-scan进行扫描,要求被扫描的对象为Component(至少被@Component注解);其中ComponentA和ComponentB为普通POJO(这里略去代码)。

测试用例如下:

@Test
public void testXml(){
    AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
    final String[] beanDefinitionNames = context.getBeanDefinitionNames();
    Arrays.stream(beanDefinitionNames).forEach(beanDefinitionName -> log.info("name contains {}.", beanDefinitionName));
}

2.2 配置类方式

@Configuration
@Import(ComponentA.class)
@ComponentScan(basePackages = "com.seong.annotation.componentscan")
public class ConfigurationA {
    @Bean
    public ComponentB beanMethodE() {
        return new ComponentB();
    }
}

如上所示,在使用@Configuration注解配置类,可以在配置类中可以通过@Import导入Bean定义,也可以通过
@ComponentScan注解进行类路径的扫描。

测试用例如下:

@Test
public void annotationConfig(){
    AbstractApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationA.class);
    final String[] beanDefinitionNames = context.getBeanDefinitionNames();
    Arrays.stream(beanDefinitionNames).forEach(beanDefinitionName -> log.info("name contains {}.", beanDefinitionName));
}

3.实现原理

章节2 中的AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext都是AbstractApplicationContext的子类,而Spring框架启动时的核心逻辑在于AbstractApplicationContext中的refresh()方法;AnnotationConfigApplicationContext、ClassPathXmlApplicationContext以refresh()方法为基础,并通过模板方法进行类一些扩展。

3.1 属性介绍

在进入refresh()方法前,对涉及的重要属性进行简要说明:
在AbstractApplicationContext对象中:

(1) private ConfigurableEnvironment environment
存储上下文关联的环境变量

(2) private final List beanFactoryPostProcessors
存储全局的beanFactoryPostProcessor对象,在容器启动之初便利调用其postProcessBeanFactory方法

(3) private ResourcePatternResolver resourcePatternResolver
资源解析器,从类路径下读取资源文件进入内存

(4) private MessageSource messageSource
国际化组件,国际化功能依赖于该对象实现

(5) private ApplicationEventMulticaster applicationEventMulticaster
事件广播器,事件能力依赖该对象进行

(6) Set> applicationListeners/earlyApplicationListeners 和Set earlyApplicationEvents
监听器收集对象,用于收集容器启动过程中的监听器,当事件广播器初始化后向其注册 private final

(7) Set earlyApplicationEvents;
收集事件对象,用于收集容器启动过程中触发的事件,当事件广播器初始化后立刻触发

在AbstractBeanFactory对象中:

(8) private final List beanPostProcessors = new BeanPostProcessorCacheAwareList();
存储全局的beanPostProcessor对象,在Bean对象初始化阶段依次调用其postProcessBeforeInitialization和postProcessAfterInitialization方法.

3.2 AbstractApplicationContext

refresh()方法的主线逻辑如下所示可被分为12个步骤,AbstractApplicationContext进行了相当程度的实现,子类也可基于此进行扩展:

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// ⚠️1.Prepare this context for refreshing.
		prepareRefresh();

		//  ⚠️2.Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

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

		try {
			//  ⚠️4.Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);

			//  ⚠️5.Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);

			//  ⚠️6.Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);

			//  ⚠️7.Initialize message source for this context.
			initMessageSource();

			//  ⚠️8.Initialize event multicaster for this context.
			initApplicationEventMulticaster();

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

			//  ⚠️10.Check for listener beans and register them.
			registerListeners();

			//  ⚠️11.Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			//  ⚠️12.Last step: publish corresponding event.
			finishRefresh();
		} catch (BeansException ex) {
			//...
		} finally {
			//...
		}
	}
}

3.3 refresh()方法介绍

3.3.1 容器刷新前的准备

protected void prepareRefresh() {
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);

	initPropertySources();
	
	getEnvironment().validateRequiredProperties();

	if (this.earlyApplicationListeners == null) {
		this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
	} else {
		this.applicationListeners.clear();
		this.applicationListeners.addAll(this.earlyApplicationListeners);
	}
	this.earlyApplicationEvents = new LinkedHashSet<>();
}

进行了容器刷新前的准备工作,如记录开始时间(用于计算启动时长)、容器启动状态的设置、属性的初始化操作。
其中,子类可扩展initPropertySources()方法,Spring web框架对该方法进行了扩展,实现从环境变量中获取属性值填充占位符。getEnvironment().validateRequiredProperties()可用于进行容器启动前的环境变量校验,要求指定的变量必须被赋值。
this.earlyApplicationEvents属性用于收集事件广播器被初始化前的事件,在广播器创建后再触发这些事件,因此需要提前被初始化;当容器启动完成后,该属性需要被再次设置为null。

3.3.2 获取beanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	refreshBeanFactory();
	return getBeanFactory();
}

refreshBeanFactory()在子类中有不同的实现,而getBeanFactory()返回的都是new出来的DefaultListableBeanFactory类型的对象。
对于AbstractRefreshableApplicationContext类型的Spring容器,refreshBeanFactory()进行了以下扩展:

protected final void refreshBeanFactory() throws BeansException {
	DefaultListableBeanFactory beanFactory = createBeanFactory();
	beanFactory.setSerializationId(getId());
	customizeBeanFactory(beanFactory);
	loadBeanDefinitions(beanFactory);
	this.beanFactory = beanFactory;
}

createBeanFactory()方法通过直接new方式创建DefaultListableBeanFactory类型的IOC容器;通过调用容器的setSerializationId方法设置serializationId属性。
重点在于customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);方法;
customizeBeanFactory方法允许对容器进行一些设置,如同名Bean是否覆盖问题、是否支持循环依赖等,如下所示:

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
	if (this.allowBeanDefinitionOverriding != null) {
		beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.allowCircularReferences != null) {
		beanFactory.setAllowCircularReferences(this.allowCircularReferences);
	}
}

loadBeanDefinitions(beanFactory)方法的功能是向IOC容器中注册BeanDefinition信息,这些BeanDefinition信息可以来自于XML配置文件、属性文件、Groovy配置文件等。

3.3.3 对beanFactory准备工作

prepareBeanFactory(beanFactory)方法为beanFactory进行容器刷新前的准备工作,可以分为如下几类:
(1)初始化Spring组件
包括类加载器BeanClassLoader、Aware处理器ApplicationContextAwareProcessor、属性编辑器PropertyEditorRegistrar、bean表达式解析器BeanExpressionResolver、监听器监测器ApplicationListenerDetector;注意:ApplicationListenerDetector在前后两次被加入到容器的beanPostProcessors属性中。

(2)beanFactory其他属性初始化
对框架引入的Aware接口,如EnvironmentAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware等,需要添加到ignoreDependencyInterfaces属性中标记不需要进行依赖检查和自动注入;因为ApplicationContextAwareProcessor组件对于实现Aware接口的类在回调过程中设置了属性信息。

(3)LTW配置
AOP切面的织入方式有三种:编译阶段,通过特殊的编译器实现,如AspectJ;类加载阶段,通过LTW实现;运行时,通过JDK或者CGLIB动态代理实现。工作中未见过LTW的实际使用场景,不是本文关注的对象。

(4)注入环境信息相关的Bean对象
包括环境对象Bean(environment),系统属性Bean(systemProperties),系统环境变量Bean(systemEnvironment),这些Bean对象的直接数据来源为System.getProperties()System.getenv(),即将机器的环境变量信息使用Bean的方式进行了包装。

3.3.4 postProcessBeanFactory

预留给子类容器扩展,在容器刷新前进行的定制化操作。

3.3.4 invokeBeanFactoryPostProcessors(beanFactory)

Spring容器按照 PriorityOrder接口 > Ordered接口 > non的顺序依次调用BeanFactoryPostProcessor对象的postProcessBeanFactory方法。该方法为容器级别,即容器启动过程中postProcessBeanFactory之后调用一次。

3.3.4 registerBeanPostProcessors(beanFactory)

Spring容器按照 PriorityOrder接口 > Ordered接口 > non的顺序依次将BeanPostProcessor加入到IOC容器的beanPostProcessors属性中。在Bean对象的初始化阶段会调用BeanPostProcessor的勾子方法,即每个Bean在创建过程中都需要经历BeanPostProcessor的装饰和处理。
另外,在该方法的最后,Spring再次将ApplicationListenerDetector加入到IOC中,读者可以在Spring系列-5 事件机制文章中找到答案。

3.3.4 initMessageSource();

初始化国际化资源,请参考:Spring系列-7 国际化

3.3.4 initApplicationEventMulticaster();

初始化Spring容器的事件广播器,请参考:Spring系列-5 事件机制

3.3.4 onRefresh();

预留给子类容器扩展,扩展向IOC容器注册单例Bean前的定制行为。SpringBoot对此方法进行了扩展,后续在介绍SpringBoot启动流程时进行详细介绍。

3.3.4 registerListeners();

protected void registerListeners() {
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}
	
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}

registerListeners()方法做了两件事件:
(1)向事件广播器注册监听器
在Spring容器的事件广播器被初始化前,向Spring容器注册的监听器都会保存在this.applicationListeners属性上:

public void addApplicationListener(ApplicationListener<?> listener) {
	Assert.notNull(listener, "ApplicationListener must not be null");
	if (this.applicationEventMulticaster != null) {
		this.applicationEventMulticaster.addApplicationListener(listener);
	}
	this.applicationListeners.add(listener);
}

因此,需要在事件广播器被初始化后,将监听器注册到广播器上:

for (ApplicationListener<?> listener : getApplicationListeners()) {
	getApplicationEventMulticaster().addApplicationListener(listener);
}

同时,从IOC中取出所有ApplicationListener类型的Bean对象,即用户自定义的监听器对象,将其注册到Spring事件广播器上:

String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
	getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

(2)触发earlyEvent
在容器的准备阶段,Spring对this.earlyApplicationEvents属性进行了初始化,即不会为空;当向Spring容器发生事件时,被记录在this.earlyApplicationEvents属性中:

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
	//...
	if (this.earlyApplicationEvents != null) {
		this.earlyApplicationEvents.add(applicationEvent);
	} else {
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}
	//...
}

在事件广播器被初始化后,需要立刻触发寄存在this.earlyApplicationEvents属性中的事件,并将this.earlyApplicationEvents属性设置为空,以保证后续的事件触发可以经过广播器,不再寄存于this.earlyApplicationEvents属性:

Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
	for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
		getApplicationEventMulticaster().multicastEvent(earlyEvent);
	}
}

3.3.4 finishBeanFactoryInitialization(beanFactory);

完成向容器中注册所有单例非lazy的bean对象的操作,请参考:Spring系列-2 Bean的生命周期、Spring系列-3 Bean实例化与依赖注入、Spring系列-4 循环依赖与三级缓存。

3.3.4 finishRefresh()

完成容器刷新后的清理工作。

你可能感兴趣的:(Spring系列,spring,java,spring,boot)