type
status
date
slug
summary
tags
category
icon
password
容器
ApplicationContext启动流程其实就是 Spring 容器的启动过程。在介绍 ApplicationContext 的启动流程之前我们先介绍一下 Spring 容器。
Spring 框架提供了多个容器的实现,这些容器分为两套体系:一套是早期的 BeanFactory 体系;还有一套是现在常用的 ApplicationContext,也可称为应用上下文,它继承了 BeanFactory,它除了有 BeanFactory 的功能外 ,还提供了其他服务,例如事务和 AOP 服务、国际化(il8n)的消息源以及应用程序事件处理等企业级的服务。
说到这,就不得不说 Spring 的两种配置方式,在早期都是 XML 配置文件的方式,而现在使用的是注解配置的方式。BeanFactory 体系的容器一般用来处理 XML 配置文件的方式,而 ApplicationContext 体系则都可以处理。
下面列出了 BeanFactory 和 ApplicationContext 两种容器的差异:
功能 | BeanFactory | ApplicationContext |
Bean 实例化时机 | BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用 getBean() ),才对该 Bean 进行加载实例化。 | ApplicationContext 采用预加载的方式注入 Bean,在启动的时候就会调用 refresh() 预加载所有单例 Bean,完成整个环境的初始化。 |
Bean 扩展 | BeanPostProcessor 和 BeanFactoryPostProcessor 手动注册 | BeanPostProcessor 和 BeanFactoryPostProcessor 自动注册 |
上下文 | 从 XML 文件中读取上下文(XmlBeanFactory) | 支持通过 ClassPath、FileSystem、Web、Annotation 等方式读取上下文,并且可以设置多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次。 |
国际化 | / | 支持国际化 i18n( MessageSource) |
访问资源 | / | 提供统一的资源文件访问方式,加载多种不同类型的资源(ResourceLoader) |
环境切换 | / | 支持多环境切换,加载多个配置文件(Environment) |
事件机制 | / | 强大的事件监听机制,当 ApplicationContext 中发布一个事件时,所有扩展ApplicationListener 的 Bean 都将接受到这个事件,并进行相应的处理。(ApplicationEvent 和 ApplicationListener) |
BeanFactory 继承体系
BeanFactory 是容器最基础的类,它定义了容器的基本功能规范:
在 BeanFactory 里只对容器的基本行为作了定义,其根本不关心你的 Bean 是如何定义怎样加载的。 正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。而要知道工厂是如何产生对象的,我们就需要看具体的容器了,也就是 BeanFactory 的子类。
BeanFactory 大致的继承关系如下:
BeanFactory 体系中常用的实现类有:
- ListableBeanFactory:提供容器中 bean 迭代的功能。如返回所有 Bean 的名字、容器中 Bean 的数量等。
- HierarchicalBeanFactory:提供父容器的访问功能,可通过 ConfigurableBeanFactory 的 setParentBeanFactory 方法设置父容器。
- AutowireCapableBeanFactory:为 Spring 容器之外的 Bean ,也就是未交由 Spring 管理的 Bean ,提供依赖注入的功能。
以上三个是 BeanFactory 的直系亲属,这个三个直系亲属下面又派生了两个复杂的容器:
- ConfigurableBeanFactory:其继承了 HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,其提供了很多方法,如:定义类加载器、类型转化、属性编辑器、注册依赖 Bean 、销毁 bean 等,且该接口被大多数的容器继承、实现。
- ConfigurableListableBeanFactory:这个接口继承了 ListableBeanFactory、 AutowireCapableBeanFactory、ConfigurableBeanFactory,自身主要提供用于分析和修改 bean 定义以及预先实例化单例 Bean 的方法。
最后是核心容器:
DefaultListableBeanFactory:它实现了以上所有的接口,在 BeanFactory 体系中可以作为一个独立的容器使用。这个类特别重要,后面我们介绍到的Spring应用上下文启动都是围绕该类展开的,这个类中有几个字段我们后面会反复使用到。此外还有继承自DefaultSingletonBeanRegistry的字段。
下面重点介绍一下ApplicationContext,主要是因为 ApplicationContext 包含了 BeanFactory,实际中基本不单独使用 BeanFactory。
ApplicationContext 继承体系
上面说过 ApplicationContext 是 BeanFactory 子类,它不仅包含 BeanFactory 所有功能,还对其进行了扩展,而我们喜欢将 ApplicationContext 称为应用上下文,因为容器只是它的基本功能。
ApplicationContext 自身提供的方法非常简单,但它继承了六个接口,来扩展自身功能:
- EnvironmentCapable:获取 Environment。
- ListableBeanFactory、HierarchicalBeanFactory:这是 BeanFactory 体系接口,分别提供 Bean 迭代和访问父容器的功能。
- MessageSource:支持国际化功能。
- ApplicationEventPublisher:应用事件发布器,封装事件发布功能的接口。
- ResourcePatternResolver:该接口继承至 ResourceLoader ,作用是加载多个 Resource。
ApplicationContext 同样提供了非常多的实现类,其又可细分为两大类, ConfigurableApplicationContext 和 WebApplicationContext。
ConfigurableApplicationContext
该接口是比较重要的一个接口,几乎所有的应用上下文都实现了该接口。该接口在ApplicationContext的基础上提供了配置应用上下文的能力,此外提供了生命周期的控制能力。
该接口下又有几个重要的实现类:
- AbstractApplicationContext:这是个抽象类,仅实现了公共的上下文特性。这个抽象类使用了模板方法设计模式,需要具体的实现类去实现这些抽象的方法。
- GenericApplicationContext:该类继承自 AbstractApplicationContext,是为通用目的设计的,它能加载各种配置文件,例如 xml,properties 等等。它的内部持有一个 DefaultListableBeanFactory 的实例,实现了 BeanDefinitionRegistry 接口,以便允许向其应用任何 bean 的定义的读取器。
- AnnotationConfigApplicationContext:该类继承自 GenericApplicationContext ,提供了注解配置(例如:@Configuration、@Component等)和类路径扫描(scan方法)的支持。
WebApplicationContext
该接口是专门为 Web 应用准备的,其允许从相对于 Web 根目录的路径中装载配置文件完成初始化。
该接口的核心实现类有:
- ConfigurableWebApplicationContext:该接口同时继承了 WebApplicationContext 和 ConfigurableApplicationContext,提供了 Web 应用上下文的可配置的能力。
- GenericWebApplicationContext:该类继承自 GenericApplicationContext,实现了 ConfigurableWebApplicationContext。
- XmlWebApplicationContext:该上下文是使用 Xml 配置文件的方式,不过是在 Web 环境中使用的。
- AnnotationConfigServletWebServerApplicationContext:该类是被 SpringBoot 扩展而来的,SpringBoot 使用的就是该上下文。
ApplicationContext 启动流程
有了上面对容器体系的基本认识,下面可以回到正题,开始介绍 ApplicationContext 的启动流程。一般我们会通过下面代码来启动一个容器。
然后会执行到下面的代码
this()
先执行
this()
方法- 在调用AnnotationConfigApplicationContext的构造方法之前,会调用父类GenericApplicationContext的无参构造函数构造一个BeanFactory
this.beanFactory = new DefaultListableBeanFactory()
。
- 构造AnnotatedBeanDefinitionReader(主要作用添加一些基础的PostProcessor,同时可以通过reader进行BeanDefinition的注册),同时对BeanFactory进行设置和添加PostProcessor(后置处理器)
- 设置dependencyComparator:AnnotationAwareOrderComparator,它是一个Comparator,是用来进行排序的,会获取某个对象上的Order注解或者通过实现Ordered接口所定义的值进行排序,在日常开发中可以利用这个类来进行排序。
- 设置autowireCandidateResolver:ContextAnnotationAutowireCandidateResolver,用来解析某个Bean能不能进行自动注入,比如某个Bean的autowireCandidate属性是否等于true
- 向BeanFactory中添加ConfigurationClassPostProcessor对应的BeanDefinition,它是一个BeanDefinitionRegistryPostProcessor,并且实现了PriorityOrdered接口
- 向BeanFactory中添加AutowiredAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessorAdapter,MergedBeanDefinitionPostProcessor
- 向BeanFactory中添加CommonAnnotationBeanPostProcessor对应的BeanDefinition,它是一个InstantiationAwareBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor
- 向BeanFactory中添加EventListenerMethodProcessor对应的BeanDefinition,它是一个BeanFactoryPostProcessor,SmartInitializingSingleton
- 向BeanFactory中添加DefaultEventListenerFactory对应的BeanDefinition,它是一个EventListenerFactory
- 构造ClassPathBeanDefinitionScanner(主要作用可以用来扫描得到并注册BeanDefinition),同时进行设置:
- 设置this.includeFilters = AnnotationTypeFilter(Component.class)
- 设置environment
- 设置resourceLoader
基本上
this()
的流程就是这些。 this()
执行完后, register(componentClasses)
就是把传进来的类注册到BeanFactory。下面重点介绍
refresh()
方法。refresh()
refresh()
是 ApplicationContext 最核心的方法,负责完成整个流程的加载。上面这个代码可能看的有点头晕,具体可以分为下面步骤
其中2、3、4、5、6 这几步是我个人觉得最核心的步骤。
1、prepareRefresh
查看代码
记录容器的启动时间,这个方法基本啥都没做,留给子类ac去做自定义的操作。
比如web环境下的ac可能会将servletConfig或者servletContext里配置的一些参数值也当做属性值加入到spring中。因为你的spring的部分配置可能会依赖这些web.xml里的配置
另外就是对必要属性的验证。如果有必要的话可以自己去写一个ac继承spring,然后重写initPropertySources方法去设置你觉得必要的属性,然后当
getEnvironment().validateRequiredProperties();
的时候如果没有找到你之前setRequiredProperties的那些属性.就会提示报错。2、ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
查看代码
getBeanFactory
方法就是获取当前这个ac里设置的bf,没啥好说的。主要看看 refreshBeanFactory
这个方法里面做了啥。查看代码
2.1 shuwdown之前的bf
如果当前这个ac已经有bf了(比如之前ac的refresh就调用过)了,那这里需要把这个bf shutdown。
比如把bf里的各种缓存的map情况,然后所有的单例bean如果impl了DisposableBean,还需要调用它的destroy方法去做相应的操作。
2.2 创建bf
DefaultListableBeanFactory beanFactory = createBeanFactory();
createBeanFactory默认返回的是DefaultListableBeanFactory类的实例。目前为止spring中还没看到其他继承这个BF的实现类,基本都是用这个类作为默认的BF。可能因为功能已经足够强大了吧。
另外因为ApplicationContext也继承了BF,所以很多操作会在ac里操作掉。根据ac的不同实现做不同的操作,可能这也是bf没有这么多实现的原因吧。
2.3 配置bf
customizeBeanFactory(beanFactory);
这是一个模板方法.允许子类ac去实现他,对bf进行一些个性化配置。比如是否需要解决单例的循环依赖呀?是否允许beanDifination多次定义被覆盖呀?
bf里配置还蛮多的,不过我看源码里目前子类ac也没啥进行定制的。
2.4 加载bd
loadBeanDefinitions(beanFactory);
BeanFactory的作用就是读取bean的配置信息变成内存中的BeanDifination。然后在创建bean的时候根据这个bd去构造具体的bean。
不同的bean的配置方式可能各种各样。比如有XML的.Properties的。传统注解@Service@Autowired的.又或是@Configuration这种java config方式的..但是他们在内存中都是通过BeanDifination来表示的(可能是不同子类)。
不同的ApplicationContext代表了不同的环境。比如适用于web环境的
WebApplicationContext,
也有传统单独使用spring的时候的XML里的 ClassPathXmlApplicationContext
等等这些不同的ac加载bd的方式也会各有不同,所以在这个loadBeanDefinitions模板方法里就是要根据不同的ac去实现不同的加载bd的方式。
比如XML的ac就应该委托
XmlBeanDefinitionReader
去读取bean形式的配置,转化成BeanDifination注册到BF中。而支持Annotation的ac就应该委托
AnnotatedBeanDefinitionReader
这种reader去扫描Java源码,注册相应的BD。当然注册的BD不一定就是我们代码里用到的那些bean对应的bd,也可能是一些BeanPostProcessor或者子接口的实现类。
因为比如InstantiationAwareBeanPostProcessor类似这种特殊的接口都是参与Spring的生命周期可以在一些必要的时候替换掉原始的bean,或者对bean的属性进行一些加工。
比如@Autowired这种操作就是用到特殊的bpp
AutowiredAnnotationBeanPostProcessor
同理@Configuration也是一样。所以说不同的bean的配置方式在spring创建对应的bean过程中走的流程可能是不一样的,这也是Spring比较灵活的地方。
有些bean会被适用于它的bpp去处理去生成或者去注入属性,而有一些可能就是比较原生,直接通过bd被BeanWrapper给new出来。
3、prepareBeanFactory
查看代码
3.1 SPEL
设置spelResolver用于spel的解析。spel单独就有一个自己的模块,具体就不说了。
3.2 PropertyEditorRegistrar
设置PropertyEditorRegistrar。spring的配置文件很多都是XML或者property这种文本,读取出来的都是字符串,但是java对象有各种类型的属性,所以生成bean的时候需要做一些类型转化。
比如 String -> Int类似这种,就需要自己去写一个PropertyEditorSupport的实现类,去impl setAsText(String) 的方法,把XML里得到的字符串转化成Java对象里的Int值。Spring自带了很多这种Editor
另外spring的类型转化也有很多种方法。这种 PropertyEditorSupport(java.beans包下的)可能是早期的方案。后续的SPring版本中有一些地方转化用到了conversionService,里面又设计到其他的一些类型转化接口。
3.3 注册BeanPostProcessor
beanPostProcessor可以在bean初始化方法(比如XML里的init-method或者InitializingBean的afterPropertySet方法),前和后对bean进行加工或者返回一个wrapper替换掉原本的bean。
注意的是bean的属性填充是在bpp2个操作之前的,就是populate(BeanFactory创建bean的一个利用BeanWrapper去设置bean的属性的时候回调用的方法)方法已经被调用过的.
Spring在这里new了一个ApplicationContextAwareProcessor
查看代码
对实现了一部分Aware的bean做方法调用。
比如你的bean impl了ApplicationContextAware这个接口,那就会调用你的setApplicationContext方法去让你有机会将ac设置为你的bean里的一个属性。
为什么Aware的一些接口要用这些内置的bpp去处理,而不是当做一般的bean注入?
我个人觉得是第一,要控制是什么时候注入这些bean。比如如果只是在一般的populate属性的时候注入这个ac,那这个时候不是所有的bean都被new过,可能会有一些意想不到的问题。
第二我觉得是因为ac和BeanFactory可能会有多个,比如早期没有springboot的时候,spring会有一个ac,然后springmvc自己也会有一个ac,有parent和child的关系。那你在一个bean里注入ac,就会有2个符合条件的ac选择,这也是有问题的。
3.4 忽略部分类型bean的注入
查看代码
为什么这些类型的bean不需要注入? 因为3.3里已经在bpp里处理了
3.5 指定部分类型的bean注入
查看代码
指定了一些bean的类型使用特定的bean注入,比如BeanFactory就使用当前这个ac对应的bf去注入即可。
3.6 额外注册一些Bean
查看代码
如果有一些bean你没配置,那就会使用spring默认的配置,比如environment,你如果配置了,那就用你的bean。
4、postProcessBeanFactory(beanFactory);
这个方法是一个模板方法,允许子类对bf做一些处理。
比如一些web相关的ac会在这里add一些bpp,比如
ServletContextAwareProcessor
来对servlet做一些处理或者在这里也可以get BF以后去调用方法增加一些BeanFactoryPostProcessor.
5、invokeBeanFactoryPostProcessors(beanFactory);
做到这个方法的时候BF已经加载完所有的BeanDifination了(详见第2节)。但是任何bean都还没有被初始化(new)。
看方法名字就知道这个方法是去激活调用beanFactoryPostProcessors的相关方法的。
beanFactoryPostProcessors这个接口还有一个子接口叫做BeanDefinitionRegistryPostProcessor。
这个子接口的postProcessBeanDefinitionRegistry方法是处理BeanDefinitionRegistry用的,可能是个新增的接口。因为我看到网上很多博客上都只写了BFPP而没有写这个接口..(因为大部分文章介绍的是早期的spring)。
这个接口的会先于BFPP的postProcessBeanFactory掉用,他可以处理BeanDefinitionRegistry。有一些BF实现了这个接口,也有一些BF没有,绝大部分ac使用的
DefaultListableBeanFactory
是实现了这个接口的,所以可以处理BeanDefinitionRegistryPostProcessor。BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry作用是给你一个机会去手动注册beanDifination。可以用这个接口去注册一些BeanFactoryPostProcessor,当然普通的BD也可以去注册或者修改。但是如果是这种目的的话最好可以使用BeanFactoryPostProcessor的postProcessBeanFactory。这样通用性更好,也更合理。
BeanFactoryPostProcessor的postProcessBeanFactory用于修改BeanFactory或者去修改BF里的BeanDifination。
一个典型的案例就是在早期版本的spring(还没用springboot的时候)我们会配置placeholderconfigurer。这个类就是实现了BeanFactoryPostProcessor接口,他会扫描所有bean里的属性,去把
${}
这种占位符替换成property文件里指定的值。常用于db的配置独立放在.properties文件中,datasource的配置放在bean.XML文件中。所以
invokeBeanFactoryPostProcessors(beanFactory);
这个方法所做的事情就是:- 获取所有手动注册的BeanDefinitionRegistryPostProcessor,去调用postProcessBeanDefinitionRegistry。所谓的手动注册就是在之前几节的模板方法中直接调用ApplicationContext的addBeanFactoryPostProcessor方法注册的BeanDefinitionRegistryPostProcessor。
- 然后获取扫描bf中所有注册的BeanDefinitionRegistryPostProcessor,按PriorityOrdered > Ordered > rest 去排序,依次调用postProcessBeanDefinitionRegistry方法,也就是说你的非手动注册的BeanDefinitionRegistryPostProcessor。如果实现了PriorityOrdered接口。那肯定会比实现了Ordered接口的BeanDefinitionRegistryPostProcessor早调用。同样接口的.就按接口内定义的顺序排序,比如Ordered接口,就是数字越小排在越前面。
- 处理一般的BeanFactoryPostProcessor的postProcessBeanFactory方法。顺序和BeanDefinitionRegistryPostProcessor一样,也是手动注册的> PriorityOrdered > Ordered > rest
查看代码
6、registerBeanPostProcessors(beanFactory);
注册BPP。
之前是BFPP用于处理BeanFactory,处理其中的BeanDifination,那这里就是注册BPP用于在处理bean了。
对于BPP并没有手动注册的方法,比如ac的addBeanPostProcessor这种方法,BFPP相关方法的调用是先于bean 初始化(new)的,很好理解,因为是对BeanFactory的处理,而不是当bean的处理.,(但是BFPP也是会在bean new之前全部被扫描从bf里找出来特殊处理...所以从这点上说好像不用手动配置也可以..)
而bpp是在bean 初始化或者填充属性过程中处理的,是在bean被new的过程中处理的,没必要手动注册,因为当前这一步仅仅是添加bpp,还不会调用bpp的相关方法(因为这个时候还没有new bean),这一点BFPP不同(BFPP直接调用相关方法),直接和一般的bean一样配置就行了。
BPP的处理顺序和BFPP一样(再次强调一下,这里只是把这些BPP的特殊的bean new出来了,并不会new 一般的bean,所以这里不会调用BPP的相关方法)。最后就是BPP也有特殊的子接口,叫做
MergedBeanDefinitionPostProcessor
,是最后处理的。所以这些 MergedBeanDefinitionPostProcessor 会在BPP的最内层(如果只是当做一般的BPP去使用相关方法的话就不要使用这个子接口,因为会影响处理的顺序)7、initMessageSource
初始化MessageSource。主要用于国际化,没怎么用过,没深入研究,大致是读取不同语言的properties根据Locale去做相应的处理。
8、initApplicationEventMulticaster
同7个人没咋用过。
ApplicationContext可以publish一些Event,你也可以注册一些Listener去监听自己感兴趣的实践。
9、onRefresh();
也是一个模板方法,给子类ac一个机会去初始化自己想要初始化的特殊的bean。
比如一些web的ac会初始化ThemeSource,用于web页面的主题展示和切换,比如jsp里的标签
<spring:theme code="style"/>
现在前后端分离了这个感觉很少用了。
10、registerListeners();
配合8,可以注册一些listener去监听你感兴趣的ApplicationEvent
11、finishBeanFactoryInitialization(beanFactory);
除了注册一些特殊的bean,比如ConversionService以外。
最重要的就是对于之前BF加载的每一个BD,如果这个bean是单例并且不是lazy的,不是abstract的(XML里可以配置一个bean是abstract.用于继承简化配置),就去初始化对应的bean,也就是通过
BeanFactory.doGetBean
方法去创建这个bean的实例。里面涉及比较复杂的步骤,涉及各种BPP的调用,还有各种情况的处理,比如循环依赖, 代理方法什么的,后面会再分享。
12、finishRefresh()
完成refresh。个人没怎么用过。
会注册LifecycleProcessor,如果你的bean实现了Lifecycle接口,在ac做stop.refresh或者start等方法的时候你的bean也会被调用这些方法,给你一些回调处理的机会。
参考
- Author:mcbilla
- URL:http://mcbilla.com/article/64f288a0-2fbf-4fc1-b2d3-5c8274a78fec
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts