在2021年之前,我在网上看过很多关于 beam 生命周期以及 AOP 相关源码的文章,并且自己简单实现了一个玩具级别的 Ioc 容器。如果说,在面试过程中,面试官跟我扯 beam 的生命周期或者 AOP 的相关实现,我是非常的自信自己可以扯一大堆东西出来。但是如果再深入的话,我就很难回答出来了。比如 @Bean、@AutoWired 是如何实现的,核心的实现类是哪个,Spring 的事件原理等等。
我对这些问题真的很好奇,以至于今年年初,带着这些问题大致又刷了一遍 Spring 源码。对 BeanFactory 甚至对 Spring 的核心思想又有了新的认识。
1. 如何理解 BeanFactory 和 ApplicationContext
ApplicationContext 继承了 BeanFactory,所以理论上 ApplicationContext 拥有 BeanFactory 的能力。除此之外,ApplicationContext 还拥有其他一些能力,比如注册 BeanDefinition( 实现了 BeanDefinitionRegistry),发布 Event(实现了 ApplicationEventPublishe)等。所以,ApplicationContext 可以理解是 BeanFactory 的超集。
另外,虽然说 ApplicationContext 继承了 BeanFactory,但是 ApplicationContext 这部分能力的内部实现是通过委派 BeanFactory 的方式去实现的,这种设计有点装饰器模式或者组合模式的味道。
2. BeanFactoryPostProcessor
As we know,BeanFactoryPostProcessor 是 BeanFactory 生命周期很重要的回调 ,该回调统一在 refresh()
中的 invokeBeanFactoryPostProcessor()
方法中。它大概的一个时机是 prepareBeanFactory()
之后,finishBeanFactoryInitialization()
(初始化BeanFactory,实例化以及初始化 bean 也在该方法)方法之前。
在 Spring 中,以来源区分的话,我理解 BeanFactoryPostProcessor 的实现有两种,分别是 Spring 内建的BeanFactoryPostProcessor(比如 ConfigurationClassPostProcessor) ,以及开发者自定义的。从 BeanFactoryPostProcessor 类型区分的话,分为一般的 BeanFactoryPostProcessor 以及 BeanDefinitionRegistryPostProcessor(继承于 BeanFactoryPostProcessor)。
而 Spring 内建的 BeanFactoryPostProcessor 注册时机在什么时候呢?比如 AnnotationConfigApplicationContext,就是在 AnnotationConfigApplicationContext 构造方法中添加了很多关于注解相关的处理器,包括 ConfigurationClassPostProcessor,AutowiredAnnotationBeanPostProcessor 等
BeanDefinitionRegistryPostProcessor 实现类很强大,在回调方法中,会传入BeanDefinitionRegistry。这意味着什么?意味着可以注册 BeanDefinition 到容器中。@PropertySources
,@ComponentScans
,@ImportResource
,@import
等注解的实现就是在该回调中实现。
3. 重新认识 BeanPostProcessor
在我以往被面试经历中,谈到 BeanPostProcessor 就会扯到 Spring 的 AOP 的实现。现在觉得,那时候对 BeanPostProcessor 的理解是真的狭隘。实际上,Spring 很多功能的实现都是通过 BeanPostProsessor 来实现的,包括一推 Aware 接口的回调(ApplicationContextAwareProcessor)、@AotuWire
、@Value
(AutowiredAnnotationBeanPostProcessor)、@PostContructor
(CommonAnnotationBeanPostProcessor) 的处理等等。
BeanPostProcessor 还存在很多子接口,针对各种回调场景新增了一些方法,环绕着bean的生命周期,AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor等都是基于 BeanPostPresessor 扩展的。
4. ApplicationEventMutiCaster 广播器
Spring 的事件也不是那么的神秘,我理解它是通过 ApplicationEventMutiCaster 来实现的。可以理解 ApplicationEventMutiCaster 是桥接 ApplicationEvent 事件 与 ApplicationListener 监听器的桥梁。
在 Spring 初始化时,会调用 initApplicationEventMulticaster()
初始化 ApplicationEventMutiCaster (它是单例的),在接下来
再调用 registerListeners()
注册 Listeners。
注册的 Listeners 包括两部分,分别是 Spring 内建的 Listeners 以及开发自定义的 Listeners。Spring 内建的 Listeners 会处理 Spring 内建的一些事件,如 ContextRefreshedEvent 等。
每一个 Listener 监听器在初始化时都会被封装成 ApplicationListener,包括自定义实现 ApplicationListener 的 bean 和被标注的 @ApplicationListener
的方法,它们最终会通过 ApplicationListenerFactory 去生成。然后通过ApplicationEventMutiCaster#addApplicationListener
添加到 ApplicationEventMutiCaster 中
在 publishEvent()
时,内部实现是通过 ApplicationEventMutiCaster#multicastEvent
广播。
广播听起来高大上,实际上就是在该方法内部,通过遍历所有的 Listeners 监听器,符合监听事件条件的就调用 invokeListener()
执行相对应监听逻辑。
5. 数据绑定
数据绑定 这个概念在任何一个成型的框架中都是特别重要的(尤其是web框架),它能让框架更多的自动化,更好容错性以及更高的编码效率。它提供的能力是:把字符串形式的参数转换成服务端真正需要的类型的转换(当然可能还包含校验)。
我理解数据绑定就是数据映射,比如 Spring mvc 的请求参数映射,XML配置 bean 时,普通属性的映射等。
在 Spring 中关于数据绑定有一个核心实现类 DataBinder。核心方法就是 bind(PropertyValues pvs)。
文章导读:聊聊Spring中的数据绑定 — DataBinder本尊(源码分析)
6. 谈谈对 converter 类型转换的理解
Converter 是 Spring 的一个类型转换核心支持接口,在很多场景都有应用。可以说,有数据绑定的场景就有数据转换。
在早期,Spring 的数据转换是扩展了 Java EE javax 的 PropertyEditor(JUI场景使用),然而 PropertyEditor 存在很多局限,比如强类型转换,仅支持简单类型转换等。随着 Spring 生态越来越成熟,功能越来越强大,PropertyEditor 已不足以支持 Spring 一些稍微复杂类型转换场景,比如1:N,集合类型转换等,再加上 PropertyEditor 的局限以及设计的缺陷,Spring 决定另起炉灶,所以后面就有了一套全新的 Spring 的类型转换 Converter(1:1),ConverterFactory(1:N),GenericConverter(支持集合类型)。
它们三都着力于完成类型转换。对于使用者而言,如果做个类型转换需要了解到这三套体系无疑成本太高,因此就有了ConversionService(全新转换服务),用于统一化底层类型转换实现的差异,对外提供统一服务,所以它也被称作类型转换的门面接口
与此同时,Spring 还新增了一个 Formatter 接口,从语义上理解它也具有类型转换(数据转换的作用),相较于 Converter<S,T>,它强调的是格式化(比如 DateFormatter)。所有的格式化器(含Printer、Parser、Formatter)都是被当作 Converter 注册的,甭管是Printer 还是 Parser,都会被适配为 GenericConverter 从而被添加到 ConverterRegistry 里面去。
所以,现在 Spring 框架中存在两套类型转换,分别是 PropertyEditor 与 ConversionService。
Spring把类型转换的能力都转嫁到 TypeConverter 这个核心 API 里面去了。在还没有 ConversionService 之前,它的类型转换动作均委托给已注册的 PropertyEditor 来完成。但自 Spring 3.0 之后,这个转换动作可能被 ConversionService 来做,也可能交给PropertyEditor 处理。
Spring 在关于类型转换这一块的设计真的非常的巧妙,非常值得我们学习并应用到工作中去。
另外推荐几篇关于 converter 系列文章:
2. Spring早期类型转换,基于PropertyEditor实现
6. 抹平差异,统一类型转换服务ConversionService
7. JDK拍了拍你:字符串拼接一定记得用MessageFormat#format
8. 格式化器大一统 – Spring的Formatter抽象
7. Spring 如何处理循环依赖
简单的说,就是三个map,分别是单例对象 singletonObjects
(我称为一级 map),早期单例引用 earlySingletonObjects
(二级 map),单例工厂 singletonFactories
(三级 map,ps:这个 map 的 value 是一个 函数式接口 ObjectFactory,通过 ObjectFactory#getObject()
可以获取到 earlySingletonObjects
)。
要回答这个问题,需要围绕着 bean 的三个核心的生命周期,分别在 bean实例化之前,bean 实例化之后,bean 初始化之后。
getBean()
时 ,在 1. bean 实例化之前,会先去一级 map 中获取,判断是否已经初始化,存在直接返回,不存在,访问二级 map。二级map 存在则返回,不存在,继续查看三级 map。三级 map 存在,调用 ObjectFactory#getObject()#getEarlyBeanReference()
返回早期引用。如果都不存在,则往下走,实例化 bean。
ps:这里有一点需要注意的是,通过三级缓存获取了 object 之后,会把该返回的 object 添加到二级 map,然后删除三级 map,
2. bean实例化之后,添加三级 map。这里为什么是添加三级 map,不是添加二级 map 呢?我理解有一个原因是为了避免重复回调SmartInstantiationAwareBeanPostProcessor 接口。
3. bean 初始化之后,添加一级 map,删除二级 map 和 三级 map。
8. Spring validator 和 bean validation
Bean Validation 是 Java EE 一套标准的数据校验 API,很多参照于 Hibernate Validation。提供了很多注解类型的数据校验的标准实现。
在 Spring 早期,数据校验也存在自己的抽象,也就是 Spring 数据校验的核心接口 Validator。但由于各种原因,Spring 的校验非常的难用,暴露很多缺陷。
还好 Spring 意识到了这个问题,接入了 Bean Validation,完成 Spring validator 的救赎。
其中,SpringValidatorAdapter 扮演了非常重要的角色(适配器模式),它是 javax.validation.Validator
到 Spring 的 Validator的适配,通过它可以对接到 Bean Validation 来完成校验工作。
文章导读:详述Spring对Bean Validation支持的核心API:Validator、SmartValidator、LocalValidatorFactoryBean…【享学Spring】
9. Spring 注解的分类、@Enable 注解驱动的应用
Spring 核心注解场景分类我大致分三类,分别是模式注解(@Repository
,@Component
,@Service
,@Configuration
等),装配注解(@ImportResource
,@Import
,@ComponentScan
),依赖注入注解(@Autowired
,@Qualifier
)
@Service
,@Configuration
可以理解是 @Component
的派生,@ComponentScan
扫描中派生了 @Component 的特性(ASM或者反射),被 @Component
以及 @Component
派生注解标注的类,都会注册到容器中。
另外还有一些组合注解、Spring 注解属性别名(如 @AliasFor
)、注解属性覆盖等类型,比如 @SpringBootApplication
就是一个组合注解,它组合了 @ComponentScan
、@EnableAutoConfiguration
、@SpringBootConfiguration
,@SpringBootApplication
存在一个 scanBasePackages
属性,该属性与 @ComponentScan
中 basePackages
互为属性别名等等。
另外,@EnableXXX
模块驱动在 Spring boot 应用中应用非常广泛,然而这并不是 Spring boot 的特性,在 Spring 中就已经实现了,并且也有实际的应用,比如 @EnableAsysn
注解驱动模式本质上就是通过 @Import
导入 一个配置类、ImportSelector 接口实现或者 ImportBeanDefinitionRegistrar 接口实现而实现的。
10. @Qualifier的分组限定
@Qualifier 一般存在限定注入以及分组限定的场景。
在分组限定使用中,自定义某个注解如 @GroupTest
(被标注了 @Qualifier
),那么在 @Bean
配置时,可通过 @GroupTest
限定注入。
11. 延迟依赖查找 ObjectProvider
ObjectProvider 一般用于延迟依赖查找。在依赖注入时,如果注入类型是 ObjectFactory 或者 ObjectProvider ,那么是直接new DependencyObjectProvider 直接返回。等到真正获取实例对象时 getIfAvailable()
才去初始化。这种延迟依赖查找在 Springboot 场景中大量应用到
另外,扯一些额外的
@Service
public class FooService {
private final FooRepository repository;
@Autowired
public FooService(FooRepository repository) {
this.repository = repository
}
}
Spring 4.3 之后,这种构造注册(支持多个构造参数)不需要显示添加 @Autowired
也可以完成注入
PS:以上均是本人学习完随口而出的白话文个人总结,如果存在不准确的描述,欢迎 dd。