• 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如上所示:可以通过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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2 配置类方式

    @Configuration
    @Import(ComponentA.class)
    @ComponentScan(basePackages = "com.seong.annotation.componentscan")
    public class ConfigurationA {
        @Bean
        public ComponentB beanMethodE() {
            return new ComponentB();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如上所示,在使用@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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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 {
    			//...
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    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<>();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

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

    3.3.2 获取beanFactory

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    	refreshBeanFactory();
    	return getBeanFactory();
    }
    
    • 1
    • 2
    • 3
    • 4

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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);
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

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

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

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

    (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);
    	}
    	//...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在事件广播器被初始化后,需要立刻触发寄存在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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3.4 finishBeanFactoryInitialization(beanFactory);

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

    3.3.4 finishRefresh()

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

  • 相关阅读:
    element 二次确认框,内容自定义处理
    Linux权限
    Application of Intelligent Motor Protector in Yeme
    上市公司财务报表精讲系列一:黄山旅游
    Java - 将TXT文本文件转换为PDF文件
    实验一. Java编程基础
    bundle文件压缩库的使用
    【MySQL】深入理解MySQL索引原理
    测试新人,如何快速上手一个陌生的系统!
    硬件设计——串联直流稳压电源
  • 原文地址:https://blog.csdn.net/Sheng_Q/article/details/128266642