Java面试知识点(八十五)仿写Spring 进阶之 AOP和IOC协作

154 篇文章 5 订阅
150 篇文章 6 订阅

前置阅读

仿照 Spring 实现简单的 IOC

仿照 Spring 实现简单的 AOP

Spring bean 的生命流程

本文承接上文,来继续说说 IOC 和 AOP 的仿写。在上文中,我实现了一个很简单的 IOC 和 AOP 容器。上文实现的 IOC 和 AOP 功能很单一,且 IOC 和 AOP 两个模块没有整合到一起。IOC 在加载 bean 过程中,AOP 不能对 bean 织入通知。在本文中,我们详细说一下进阶版 IOC 和 AOP。这个版本的实现包含了在上篇中所说的功能,这里再重述一下,如下:

  • 根据 xml 配置文件加载相关 bean
  • 对 BeanPostProcessor 类型的 bean 提供支持
  • 对 BeanFactoryAware 类型的 bean 提供支持
  • 实现了基于 JDK 动态代理的 AOP
  • 整合了 IOC 和 AOP,使得二者可很好的协同工作

一、IOC 的实现

1. BeanFactory 的生命流程

  • BeanFactory 加载 Bean 配置文件,将读到的 Bean 配置封装成 BeanDefinition 对象
  • 将封装好的 BeanDefinition 对象注册到 BeanDefinition 容器中
  • 注册 BeanPostProcessor 相关实现类到 BeanPostProcessor 容器中
  • BeanFactory 进入就绪状态
  • 外部调用 BeanFactory 的 getBean (String name) 方法,BeanFactory 着手实例化相应的 bean
  • 重复步骤 3 和 4,直至程序退出,BeanFactory 被销毁

上面简单罗列了 BeanFactory 的生命流程,也就是 IOC 容器的生命流程。接下来就来围绕上面的流程展开讨论。


2.BeanDefinition 及其他一些类的介绍

在详细介绍 IOC 容器的工作原理前,这里先介绍一下实现 IOC 所用到的一些辅助类,包括 BeanDefinition、BeanReference、PropertyValues、PropertyValue。这些类与接下来的 xml 的解析紧密相关。按照顺序,先从 BeanDefinition 开始介绍。


【BeanDefinition】

从字面意思上翻译成中文就是 “Bean 的定义”。从翻译结果中就可以猜出这个类的用途,即根据 Bean 配置信息生成相应的 Bean 详情对象。举个例子,如果把 Bean 比作是电脑?,那么 BeanDefinition 就是这台电脑的配置清单。

上面那个例子还是比较贴切的,但是只是个例子,和实际还是有差距的。那么在具体实现中,BeanDefinition 和 xml 是怎么对应的呢?答案在下面:

在这里插入图片描述


【BeanReference】

接下来我们来说说上图中的 ref 对应的 BeanReference 对象。BeanReference 对象保存的是 bean 配置中 ref 属性对应的值,在后续 BeanFactory 实例化 bean 时,会根据 BeanReference 保存的值去实例化 bean 所依赖的其他 bean。


【PropertyValues】、【PropertyValue】

接下来说说 PropertyValues 和 PropertyValue 这两个长的比较像的类,首先是 PropertyValue。PropertyValue 中有两个字段 name 和 value,用于记录 bean 配置中的 标签的属性值。然后是 PropertyValues,PropertyValues 从字面意思上来看,是 PropertyValue 复数形式,在功能上等同于 List。那么为什么 Spring 不直接使用 List,而自己定义一个新类呢?答案是要获得一定的控制权,看下面的代码:

public class PropertyValues {

    private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();

    public void addPropertyValue(PropertyValue pv) {
        // 在这里可以对参数值 pv 做一些处理,如果直接使用 List,则就不行了
        this.propertyValueList.add(pv);
    }

    public List<PropertyValue> getPropertyValues() {
        return this.propertyValueList;
    }

}

3.xml 的解析

BeanFactory 初始化时,会根据传入的 xml 配置文件路径加载并解析配置文件。但是加载和解析 xml 配置文件这种脏活累活,BeanFactory 可不太愿意干,它只想高冷的管理容器中的 bean。于是 BeanFactory 将加载和解析配置文件的任务委托给专职人员 BeanDefinitionReader 的实现类 XmlBeanDefinitionReader 去做。那么 XmlBeanDefinitionReader 具体是怎么做的呢?XmlBeanDefinitionReader 做了如下几件事情:

  1. 将 xml 配置文件加载到内存中
  2. 获取根标签 <beans> 下所有的<bean> 标签
  3. 遍历获取到的<bean> 标签列表,并从标签中读取 id,class 属性
  4. 创建 BeanDefinition 对象,并将刚刚读取到的 id,class 属性值保存到对象中
  5. 遍历 <bean> 标签下的 <property> 标签,从中读取属性值,并保持在 BeanDefinition 对象中
  6. 将 <id, BeanDefinition> 键值对缓存在 Map 中,留作后用
  7. 重复 3、4、5、6 步,直至解析结束

4.注册 BeanPostProcessor

BeanPostProcessor 接口是 Spring 对外拓展的接口之一,其主要用途提供一个机会,让开发人员能够插手 bean 的实例化过程。通过实现这个接口,我们就可在 bean 实例化时,对 bean 进行一些处理。比如,我们所熟悉的 AOP 就是在这里将切面逻辑织入相关 bean 中的。正是因为有了 BeanPostProcessor 接口作为桥梁,才使得 AOP 可以和 IOC 容器产生联系

接下来说说 BeanFactory 是怎么注册 BeanPostProcessor 相关实现类的。

XmlBeanDefinitionReader 在完成解析工作后,BeanFactory 会将它解析得到的 <id, BeanDefinition> 键值对注册到自己的 beanDefinitionMap 中。BeanFactory 注册好 BeanDefinition 后,就立即开始注册 BeanPostProcessor 相关实现类。这个过程比较简单:

  1. 根据 BeanDefinition 记录的信息,寻找所有实现了 BeanPostProcessor 接口的类。
  2. 实例化 BeanPostProcessor 接口的实现类
  3. 将实例化好的对象放入 List 中
  4. 重复 2、3 步,直至所有的实现类完成注册

5.getBean 过程解析

在完成了 xml 的解析、BeanDefinition 的注册以及 BeanPostProcessor 的注册过程后。BeanFactory 初始化的工作算是结束了,此时 BeanFactory 处于就绪状态,等待外部程序的调用。

外部程序一般都是通过调用 BeanFactory 的 getBean (String name) 方法来获取容器中的 bean。BeanFactory 具有延迟实例化 bean 的特性,也就是等外部程序需要的时候,才实例化相关的 bean。这样做的好处是比较显而易见的,第一是提高了 BeanFactory 的初始化速度,第二是节省了内存资源。

6.一个完整的 Spring bean 实例化过程图
在这里插入图片描述


二、实现 AOP

AOP 是基于动态代理模式实现的,具体实现上可以基于 JDK 动态代理或者 Cglib 动态代理。其中 JDK 动态代理只能代理实现了接口的对象,而 Cglib 动态代理则无此限制。所以在为没有实现接口的对象生成代理时,只能使用 Cglib。在 toy-spring 项目中,暂时只实现了基于 JDK 动态代理的代理对象生成器。

关于 AOP 原理这里就不多说了,下面说说 toy-spring 中 AOP 的实现步骤。还是像上面一样,先列流程:

  • AOP 逻辑介入 BeanFactory 实例化 bean 的过程
  • 根据 Pointcut 定义的匹配规则,判断当前正在实例化的 bean 是否符合规则
  • 如果符合,代理生成器将切面逻辑 Advice 织入 bean 相关方法中,并为目标 bean 生成代理对象
  • 将生成的 bean 的代理对象返回给 BeanFactory 容器,到此,AOP 逻辑执行结束
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值