写点什么

清华师哥用源码解析一份 Spring 笔记,B 站上线 2 小时转发 2w,快收藏

发布于: 45 分钟前
清华师哥用源码解析一份Spring笔记,B站上线2小时转发2w,快收藏

今日分享开始啦,请大家多多指教~

一、前言

大佬的代码,:一个类实现了多个接口、继承的类又继承了其他类、接口还可以和接口继承、实现接口的抽象类再由类实现抽象类方法、类 A 继承的类 B 实现了类 A 实现的接口 C,等等。

看上去复杂又难懂的代码,却又能一次次满足需求的高效迭代和顺利扩展,而像螺丝钉一样搬砖的你,只是在大佬写的代码里,完成某个接口下的一小块功能,甚至写完了也不知道怎么就被调用运行了,整个过程像看 Spring 源码一样神奇,跳来跳去的摸不着头绪!

其实这主要是因为你的代码是否运用了设计模式,当然设计模式也没那么神奇,就像你们两家都是 120 平米的房子,他家有三室两厅一厨一卫,南北通透,全阳采光。但你家就不一样了,你家是锅碗瓢盆、卫浴马桶、沙发茶几还有那 1.8 的双人床,在 120 平米的房子里敞开了放,没有动静隔离,也没有干湿分离,纯自由发挥。所以你的代码看上去就乱得很!

二、目标

目前已实现的 Spring 框架,在 Bean 操作上能提供出的能力,包括:Bean 对象的定义和注册,以及在操作 Bean 对象过程中执行的,BeanFactoryPostProcessor、BeanPostProcessor、InitializingBean、DisposableBean,以及在 XML 新增的一些配置处理,让我们可以 Bean 对象有更强的操作性。

那么,如果我们想获得 Spring 框架提供的 BeanFactory、ApplicationContext、BeanClassLoader 等这些能力做一些扩展框架的使用时该怎么操作呢。所以我们本文希望在 Spring 框架中提供一种能感知容器操作的接口,如果谁实现了这样的一个接口,就可以获取接口入参中的各类能力。

三、设计

如果说我希望拿到 Spring 框架中一些提供的资源,那么首先需要考虑以一个什么方式去获取,之后你定义出来的获取方式,在 Spring 框架中该怎么去承接,实现了这两项内容,就可以扩展出你需要的一些属于 Spring 框架本身的能力了。

在关于 Bean 对象实例化阶段我们操作过一些额外定义、属性、初始化和销毁的操作,其实我们如果像获取 Spring 一些如 BeanFactory、ApplicationContext 时,也可以通过此类方式进行实现。那么我们需要定义一个标记性的接口,这个接口不需要有方法,它只起到标记作用就可以,而具体的功能由继承此接口的其他功能性接口定义具体方法,最终这个接口就可以通过 instanceof 进行判断和调用了。

  • 定义接口 Aware,在 Spring 框架中它是一种感知标记性接口,具体的子类定义和实现能感知容器中的相关对象。也就是通过这个桥梁,向具体的实现类中提供容器服务

  • 继承 Aware 的接口包括:BeanFactoryAware、BeanClassLoaderAware、BeanNameAware 和 ApplicationContextAware,当然在 Spring 源码中还有一些其他关于注解的,不过目前我们还是用不到。

  • 在具体的接口实现过程中你可以看到,一部分(BeanFactoryAware、BeanClassLoaderAware、BeanNameAware)在 factory 的 support 文件夹下,另外 ApplicationContextAware 是在 context 的 support 中,这是因为不同的内容获取需要在不同的包下提供。所以,在 AbstractApplicationContext 的具体实现中会用到向 beanFactory 添加 BeanPostProcessor 内容的 ApplicationContextAwareProcessor 操作,最后由 AbstractAutowireCapableBeanFactory 创建 createBean 时处理相应的调用操作。

四、实现

Spring 感知接口的设计和实现类关系,如图 9-2

  • 以上整个类关系就是关于 Aware 感知的定义和对容器感知的实现。

  • Aware 有四个继承的接口,其他这些接口的继承都是为了继承一个标记,有了标记的存在更方便类的操作和具体判断实现。

  • 另外由于 ApplicationContext 并不是在 AbstractAutowireCapableBeanFactory 中 createBean 方法下的内容,所以需要向容器中注册 addBeanPostProcessor ,再由 createBean 统一调用 applyBeanPostProcessorsBeforeInitialization 时进行操作。

2. 定义标记接口

cn.bugstack.springframework.beans.factory.Aware

在 Spring 中有特别多类似这样的标记接口的设计方式,它们的存在就像是一种标签一样,可以方便统一摘取出属于此类接口的实现类,通常会有 instanceof 一起判断使用。

3. 容器感知类

3.1 BeanFactoryAware

cn.bugstack.springframework.beans.factory.BeanFactoryAware

Interface to be implemented by beans that wish to be aware of their owning {@link BeanFactory}.

实现此接口,既能感知到所属的 BeanFactory

3.2 BeanClassLoaderAware

cn.bugstack.springframework.beans.factory.BeanClassLoaderAware

Callback that allows a bean to be aware of the bean{@link ClassLoader class loader}; that is, the class loader used by the present bean factory to load bean classes.

实现此接口,既能感知到所属的 ClassLoader

3.3 BeanNameAware

cn.bugstack.springframework.beans.factory.BeanNameAware

Interface to be implemented by beans that want to be aware of their bean name in a bean factory.

实现此接口,既能感知到所属的 BeanName

3.4 ApplicationContextAware

cn.bugstack.springframework.context.ApplicationContextAware

Interface to be implemented by any object that wishes to be notifiedof the {@link ApplicationContext} that it runs in.

实现此接口,既能感知到所属的 ApplicationContext

4. 包装处理器(ApplicationContextAwareProcessor)

cn.bugstack.springframework.context.support.ApplicationContextAwareProcessor

由于 ApplicationContext 的获取并不能直接再创建 Bean 时候就可以拿到,所以需要在 refresh 操作时,把 ApplicationContext 写入到一个包装的 BeanPostProcessor 中去,再由 AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization 方法调用。

5. 注册 BeanPostProcessor

cn.bugstack.springframework.context.support.AbstractApplicationContext

refresh() 方法就是整个 Spring 容器的操作过程,本次新增加了关于 addBeanPostProcessor 的操作。

添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext。

6. 感知调用操作

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory




这里我们去掉了一些类的内容,只保留关于本次 Aware 感知接口的操作。

首先在 initializeBean 中,通过判断 bean instanceof Aware,调用了三个接口方法,BeanFactoryAware.setBeanFactory(this)、BeanClassLoaderAware.setBeanClassLoader(getBeanClassLoader())、BeanNameAware.setBeanName(beanName),这样就能通知到已经实现了此接口的类。

另外我们还向 BeanPostProcessor 中添加了 ApplicationContextAwareProcessor,此时在这个方法中也会被调用到具体的类实现,得到一个 ApplicationContex 属性。

五、测试

1. 事先准备

cn.bugstack.springframework.test.bean.UserDao

cn.bugstack.springframework.test.bean.UserService

UserDao 本次并没有什么改变,还是提供了关于初始化的方法,并在 Spring.xml 中提供 init-method、destroy-method 配置信息。

UserService 新增加,BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware,四个感知的实现类,并在类中实现相应的接口方法。

2. 配置文件

基础配置,无 BeanFactoryPostProcessor、BeanPostProcessor,实现类

3. 单元测试

测试方法中主要是添加了一些关于新增 Aware 实现的调用,其他不需要调用的也打印了相应的日志信息,可以在测试结果中看到。

测试结果

从测试结果可以看到,本文新增加的感知接口对应的具体实现(BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware),已经可以如期输出结果了。

六、总结

目前关于 Spring 框架的实现中,某些功能点已经越来越趋向于完整,尤其是 Bean 对象的生命周期,已经有了很多的体现。整体总结如图 9-3

本文关于 Aware 感知接口的四个集成接口 BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware 的实现,又扩展了 Spring 的功能。如果你有做过关于 Spring 中间件的开发那么一定会大量用到这些类,现在你不只是用过,而且还知道他们都是什么时候触达的,在以后想排查类的实例化顺序也可以有一个清晰的思路了。

每一次内容的实现都是在以设计模式为核心的结构上填充各项模块的功能,单纯的操作编写代码并不会有太多收获,一定是要理解为什么这么设计,这么设计的好处是什么,怎么就那么多接口和抽象类的应用,这些才是 Spring 框架学习的核心所在。

今日份分享已结束,请大家多多包涵和指点!

用户头像

还未添加个人签名 2021.04.20 加入

Java工具与相关资料获取等WX: gsh950924(备注来源)

评论

发布
暂无评论
清华师哥用源码解析一份Spring笔记,B站上线2小时转发2w,快收藏