从本文开始,开启一个新的专题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启动流程,重点在于容器的刷新过程。
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容器的初始化、事件组件、国际化组件等。
如果准备开始阅读源码,第一件事应该写一个demo
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));
}
@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));
}
章节2 中的AnnotationConfigApplicationContext或者ClassPathXmlApplicationContext都是AbstractApplicationContext的子类,而Spring框架启动时的核心逻辑在于AbstractApplicationContext中的refresh()方法;AnnotationConfigApplicationContext、ClassPathXmlApplicationContext以refresh()方法为基础,并通过模板方法进行类一些扩展。
在进入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方法.
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 {
//...
}
}
}
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。
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配置文件等。
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的方式进行了包装。
预留给子类容器扩展,在容器刷新前进行的定制化操作。
Spring容器按照 PriorityOrder接口 > Ordered接口 > non的顺序依次调用BeanFactoryPostProcessor对象的postProcessBeanFactory方法。该方法为容器级别,即容器启动过程中postProcessBeanFactory之后调用一次。
Spring容器按照 PriorityOrder接口 > Ordered接口 > non的顺序依次将BeanPostProcessor加入到IOC容器的beanPostProcessors属性中。在Bean对象的初始化阶段会调用BeanPostProcessor的勾子方法,即每个Bean在创建过程中都需要经历BeanPostProcessor的装饰和处理。
另外,在该方法的最后,Spring再次将ApplicationListenerDetector加入到IOC中,读者可以在Spring系列-5 事件机制文章中找到答案。
初始化国际化资源,请参考:Spring系列-7 国际化
初始化Spring容器的事件广播器,请参考:Spring系列-5 事件机制
预留给子类容器扩展,扩展向IOC容器注册单例Bean前的定制行为。SpringBoot对此方法进行了扩展,后续在介绍SpringBoot启动流程时进行详细介绍。
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);
}
}
完成向容器中注册所有单例非lazy的bean对象的操作,请参考:Spring系列-2 Bean的生命周期、Spring系列-3 Bean实例化与依赖注入、Spring系列-4 循环依赖与三级缓存。
完成容器刷新后的清理工作。