再扯 Spring 源码_白话文

在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 系列文章:

1. 揭秘Spring类型转换 - 框架设计的基石

2. Spring早期类型转换,基于PropertyEditor实现

3. 搞定收工,PropertyEditor就到这

4. 上新了Spring,全新一代类型转换机制

5. 穿过拥挤的人潮,Spring已为你制作好高级赛道

6. 抹平差异,统一类型转换服务ConversionService

7. JDK拍了拍你:字符串拼接一定记得用MessageFormat#format

8. 格式化器大一统 – Spring的Formatter抽象

9. 细节见真章,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 属性,该属性与 @ComponentScanbasePackages 互为属性别名等等。

另外,@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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值