前言
在 上篇 实现了 判断一个类的方式是符合配置的 pointcut 表达式、根据一个 Bean 的名称和方法名,获取 Method 对象、实现了 BeforeAdvice、AfterReturningAdvice 以及 AfterThrowingAdvice并按照指定次序调用 等功能,这篇再来看看剩下的 代理对象如何生成、根据 XML 配置文件生成 BeanDefintion以及如何将生成的代理对象放入到容器中 等功能,话不多说,下面进入主题。
代理对象生成
代理对象的生成策略和 Spring 框架一致,当被代理类实现了接口时采用 JDK 动态代理的方式生成代理对象,被代理对象未实现接口时使用 CGLIB 来生成代理对象,为了简单起见这里不支持手动指定生成代理对象的策略,JDK 动态代理的实现这里不在介绍,感兴趣可以自己实现一下,这里主要讨论 CGLIB 的生成方式。
基于面向接口编程的思想,这里的生成代理对象需要定义一个统一的接口,不管是 CGLIB 生成方式还是JDK 动态代理生成方式都要实现该接口。生成代理对象是根据一些配置去生成的,同样,这里生成代理的配置也可以抽取一个统一的接口,在实现类中定义拦截器(也就是 Advice)以及实现的接口等,CGLIB 的基本使用可以到官网自行查找。代理对象生成的整体的类图如下:
其中代理创建的工厂接口 AopProxyFactory 如下,提供了不指定 ClassLoader(使用默认的 ClassLoader)和指定 ClassLoader 两种方式创建代理对象,源码如下:
1 | /** |
使用 CGLIB 创建代理的工厂接口实现类如下所示:
1 | /** |
整体来看还是比较简单的,主要是 CGLIB 第三方字节码生成库的基本用法,当然,前提是你已经了解了 CGLIB 的基本使用。AOP 的相关配置接口 Advised 相对来说就比较简单了,主要是一些相关属性的增、删、改等操作,主要部分代码如下:
1 | /** |
实现类也比较简单,代码如下:
1 | /** |
到这里,代理对象使用 CGLIB 生成的方式就已经实现了,核心代码其实比较简单,主要是需要多考虑考虑代码后期的扩展性。
创建 BeanDefinition
我们先来看看一般 AOP 在 XML 配置文件中是如何定义的,一个包含 BeforeAdvice、AfterReturningAdvice以及AfterThrowingAdvice 的 XML 配置文件如下:
1 |
|
有了之前解析 XML 的 Bean 定义的经验后,很显然这里我们需要一个数据结构去表示这个 AOP 配置,如果你阅读过 上篇 的话,类 AspectJExpressionPointcut 表示的是 <aop:pointcut id=”placeOrder” expression=”execution(* cn.mghio.service.version5.*.placeOrder(..))”/>,另外几个 Advice 配置分别对应 AspectJBeforeAdvice、AspectJAfterReturningAdvice以及 AspectJAfterThrowingAdvice 等几个类。
这里只要解析 XML 配置文件,然后使用对应的 Advice 的构造器创建对应的对象即可,解析 XML 使用的是 dom4j,主要部分代码如下所示:
1 | /** |
创建 BeanDefinition 已经完成了,现在可根据 XML 配置文件解析出对应的 BeanDefintion 了,下面只需要在合适的时机将这些 BeanDefinition 放到容器中就完成了全部流程了。
如何放到容器中
该如何把解析出来的 BeanDefintion 放到容器当中去呢?我们知道在 Spring 框架当中提供了很多的“钩子函数”,可以从这里入手,Bean 的生命周期如下:
选择在 Bean 实例化完成之后 BeanPostProcessor 的 postProcessAfterInitialization() 方法创建代理对象,AOP 使用的是 AspectJ,将创建代理对象的类命名为 AspectJAutoProxyCreator,实现 BeanPostProcessor 接口,处理代理对象的创建,AspectJAutoProxyCreator 类的核心源码如下:
1 | /** |
最后别忘了,这里的 BeanPostProcessor 接口是我们新加的,需要到之前定义的 DefaultFactoryBean 中加上对 BeanPostProcessor 的处理逻辑,主要修改如下:
1 | public class DefaultBeanFactory extends AbstractBeanFactory implements BeanDefinitionRegistry { |
最后运行事先测试用例,正常通过符合预期。
总结
本文主要介绍了 AOP 代理对象生成、解析 XML 配置文件并创建对应的 BeanDefinition 以及最后注入到容器中,只是介绍了大体实现思路,具体代码实现已上传 mghio-spring,感兴趣的朋友可以参考,到这里,AOP 实现部分已经全部介绍完毕。