前言
哈喽,大家好,我是janker。
最近总有小伙伴提到搞点Spring相关文章,为了满足小伙伴的愿望,那么今天它来了。
说到Spring,常问的知识点,AOP、IOC、循环依赖等等等。。。那么具体这些如何串联起来的呢?下面就会结合一个造车小故事详细并形象跟大家说一说。
核心概念
下面说下本篇牵涉到的核心概念,其他没涉及到的暂时先不提。
下面每个概念都会以造车为例子。
BeanDefinition
官方解释一大堆,定制Bean
的配置元信息接口诸如此类的解释。晦涩难懂,咱们就抽象叫他生产汽车零件的图纸。
BeanFactory
bean
工厂,主要作用是生产bean
,Spring
本质上是一个bean
工厂或者bean
容器,在造车的例子中我们可以抽象的叫它生产汽车的车间。
ApplicationContext
除了包含BeanFactory
所有功能以外,还包含国际化、Spring
中的环境等功能。在造车的例子中我们可以抽象的叫它生产汽车的工厂(这里的工厂区别于上面的BeanFactory
不仅生产、还有一些周边功能)。
BeanPostProcessor
BeanPostProcessor
是Spring
IOC
容器给我们提供的一个扩展接口。里面包含两个重要的方法,postProcessBeforeInitialization
和postProcessAfterInitialization
,Spring的核心拓展接口,与很多中间件结合都使用到它。在造车的例子中我们可以抽象的叫它汽车生产过程中的拓展接口,方便更换零件或者增加其他功能。
功能注解
类似@Lazy
@Scope
@DependsOn
@Primary initMethodName
等用来描述bean
相关特性的注解或者字段。一般都用这些注解或者字段来描述造车图纸的功能。
XXX Aware
Aware
是感知的意思,顾名思义只要是实现了XXXXAware
就能在本类中拿到想要的XXXX。举个例子,例如ApplicationContextAware
只要我们的类实现了ApplicationContextAware
我们就能拿到当前容器的ApplicationContext
。以此类推。
其他类就不一一介绍了。
Spring启动整体流程
整体流程
咱们以AnnotationConfigApplicationContext
为例,其他ApplicationContext
过程部分步骤稍有不同,其他主流程类似。
-
构建
ApplicationContext
-
初始化 AnnotatedBeanDefinitionReader
注册注解配置处理类(创世纪类),如AutowiredAnnotationBeanPostProcessor
、ConfigurationClassPostProcessor
、CommonAnnotationBeanPostProcessor
、PersistenceAnnotationBeanPostProcessor
、EventListenerMethodProcessor
、DefaultEventListenerFactory
等。 -
初始化 ClassPathBeanDefinitionScanner
,设置Environment
,设置ResourceLoader
-
扫描。扫描
basePackage
,注册Bean
定义 -
刷新容器(整个过程中最重要的阶段)
-
创建
beanFactory
(没有才创建、存在就获取)、 -
向
beanFactory
中添加早期要用到的BeanPostProcessor
,(ApplicationContextAwareProcessor
、ApplicationListenerDetector
等)注册一些系统相关类(系统环境、系统属性等) -
BeanFactory
后置处理,一般都是重写ApplicationContext#postProcessBeanFactory
,不同的ApplicationContext
添加的逻辑不太一样,一般都是添加跟ApplicationContext
具体实现类相关的BeanPostProcessor
处理类、忽视一些依赖接口、注册一些跟本ApplicationContext
相关的类。拿
AnnotationConfigApplicationContext
举例,它并没有重写相关抽象方法。 -
invokeBeanFactoryPostProcessors
,一句话概括调用BeanDefinitionRegistryPostProcessors
,从注册表中的配置类派生进一步的bean定义 -
注册拦截Bean创建的Bean处理器(各种
BeanPostProcessor
) -
initMessageSource
,初始化MessageSource
(国际化相关Bean
) -
initApplicationEventMulticaster
,初始化应用event
多播器,注册ApplicationEventMulticaster(应用event
事件相关) -
初始化Bean(非懒加载Bean)
-
完成刷新(刷新后的完成工作,cache清除、注册一些必要的Bean(
LifecycleProcessor
)、发送刷新完成事件、) -
准备刷新 (初始化属性源、校验一些必填属性)
-
准备
beanFactory
初始化(创建)Bean过程有很多子流程,简单概括一下:
-
获取bean。上来就从缓存中拿Bean,存在就返回、不存在就看下这个
bean
是否正在创建。(使用一、二、三级缓存解决循环依赖问题) -
创建bean(
doCreateBean
)。 -
在创建bean之前有相应的拓展点(对应接口 InstantiationAwareBeanPostProcessor
,拓展方法为postProcessBeforeInstantiation
),在创建前,会遍历所有InstantiationAwareBeanPostProcessor
的实现类,执行postProcessBeforeInstantiation
方法,举例:AbstractAutoProxyCreator
(AOP相关),PersistenceAnnotationBeanPostProcessor
(ORM持久化相关)等 -
创建 beanInstance
过程中,调用determineConstructorsFromBeanPostProcessors
方法,拓展接口为SmartInstantiationAwareBeanPostProcessor
执行的拓展方法为determineCandidateConstructors
。指定实例化的构造函数。 -
实例化bean。
-
实例化bean过程中,调用 applyMergedBeanDefinitionPostProcessors
,这里也是一个拓展点,对应接口MergedBeanDefinitionPostProcessor
,调用的拓展方法为postProcessMergedBeanDefinition
。 -
实例化过程中,调用 getEarlyBeanReference
,这里面也是一个拓展点,对应接口为:SmartInstantiationAwareBeanPostProcessor
,调用的拓展方法为getEarlyBeanReference
。(解决循环引用) -
属性赋值。属性赋值过程中也包含两个拓展点。
-
属性赋值过程中,循环遍历 InstantiationAwareBeanPostProcessor
的实现类,调用postProcessAfterInstantiation
方法,作用可以中止赋值 -
属性赋值过程中,循环遍历 InstantiationAwareBeanPostProcessor
的实现类,调用postProcessPropertyValues
方法,作用:注入属性,@Autowired
注解在这里进行依赖注入。 -
初始化。初始化过程中包含4个可以拓展的地方。
-
一类是实现 InitializingBean
接口,调用拓展方法afterPropertiesSet
来实现拓展。 -
一类是 invokeCustomInitMethod
,我们自己定义的init
方法。 -
初始化过程中,调用
invokeAwareMethods
一堆Aware方法(一般都是实现Aware接口),主要是为bean
设置beanName
、BeanClassLoader
、BeanFactory
。 -
正式初始化前,调用
applyBeanPostProcessorsBeforeInitialization
方法,还是老套路,遍历BeanPostProcessor
的实现类,调用拓展方法postProcessBeforeInitialization
。举几个比较简单的类
ApplicationContextAwareProcessor
调用postProcessBeforeInitialization
是为了执行一些Aware
方法。InitDestroyAnnotationBeanPostProcessor
实现类调用postProcessBeforeInitialization
方法是为了执行@PostConstruct
方法。剩下的就不一一列举了。 -
正式初始化方法,
invokeInitMethods
作用调用初始化方法(说了跟没说一样)。大致包含两类注意:从上面的流程我们可以得出:初始化的相关方法执行顺序如下:
PostConstruct
>afterPropertiesSet
> 我们自己定义的初始化方法。 -
初始化后,调用
applyBeanPostProcessorsAfterInitialization
,这里也是一个拓展点,换汤不换药,拓展接口为BeanPostProcessor
,调用的拓展方法为postProcessAfterInitialization
,顾名思义就是在初始化完成后要执行的操作。 -
添加到缓存中。
小结及流程图
整体过程还是比较复杂的,其实为了简化我们的理解,我们提炼出来几个比较重要的点:
-
准备了一些创世纪类,相当于造车的原始车床零部件。 -
读取了一些 bean
定义,这个bean
定义也有两种状态:一种是概念态(描述功能),一种是定义态(把功能转化为图纸) -
准备了BeanFactory,相当于制造零件的车间,在真正开始创建对象前,可以对BeanFactory做统一的拓展,以拓展额外统一的功能 -
开始生产Bean,在生产bean的前期、中期、后期添加了各种拓展接口,来实现我们需要的功能。(AOP的功能我们暂时先不详细说下一篇会详细带小伙伴去看一下。)
流程图如下图所示。
图中九个后置处理器调用的地方是核心,spring与其他组件结合很多都是基于这些拓展点。
造车小故事
上面讲了那么多概念以及各种拓展点,还是比较晦涩,不是那么好理解。下面用一个小故事,来把整个流程串联起来,就拿假药停造车的例子来说。
-
贾某圈了一笔钱,买了第一个机床(创世纪类)、第一厂房(ApplicationContext) -
贾某造车时的准备工作,(注册配置类、初始化操作) -
贾某要想造车,必定要有一些车子基础和特有的功能,比如几座、马力、续航、智能交互,这都是概念功能阶段。 -
贾某手下的大将将功能转化为生产零件图纸。(功能转化为Bean定义) -
拿着图纸开始造零件 -
造各种零件过程中,前中后开了一些拓展接口(拓展点),便于更换和DIY(更长的续航、更高的配置)。(拿钱造零件、造车) -
当然造车过程中钱是少不了的,等内存紧缺的时候也就是没钱的时候自然就继续不下去。 -
销毁零件的过程中,也伴随着一些销毁方法,比如零件的资源再利用。
在这整个造车过程中,图纸、厂房、机床、拓展接口、钱都是少不了的,同样的BeanDefinition、ApplicationContext、创世纪类、内存也都是必不可少的。上述故事是为了让大家更好理解,也希望小伙伴结合小故事对spring有新的认识。
总结
从spring启动流程,到贾某造车,大家对spring的拓展机制以及启动过程应该有大概的认识了。针对AOP及IOC的具体实现细节将在下一节为大家呈现,敬请期待。
原文始发于微信公众号(爪哇干货分享):Spring与造车的关系
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/172022.html