专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  快!快!快!DeepSeek 满血版真是快 ·  昨天  
码农翻身  ·  中国的大模型怎么突然间就领先了? ·  15 小时前  
程序员的那些事  ·  OpenAI ... ·  2 天前  
程序员的那些事  ·  印度把 DeepSeek ... ·  3 天前  
程序员的那些事  ·  成人玩偶 + ... ·  5 天前  
51好读  ›  专栏  ›  SegmentFault思否

徒手撸框架 ——实现 Aop

SegmentFault思否  · 公众号  · 程序员  · 2018-02-10 08:00

正文

原文地址:犀利豆的博客(https://www.xilidou.com/2018/01/13/spring-aop/)

上一讲我们讲解了 Spring 的 IoC 实现。大家可以去我的博客查看(https://www.xilidou.com/2018/01/08/spring-ioc/),这一讲我们继续说说 Spring 的另外一个重要特性 AOP。之前在看过的大部分教程,对于 Spring Aop 的实现讲解的都不太透彻,大部分文章介绍了 Spring Aop 的底层技术使用了动态代理,至于 Spring Aop 的具体实现都语焉不详。这类文章看以后以后,我脑子里浮现的就是这样一个画面:

我的想法就是,带领大家,首先梳理 Spring Aop 的实现,然后屏蔽细节,自己实现一个 Aop 框架。加深对 Spring Aop 的理解。在了解上图 1-4 步骤的同时,补充 4 到 5 步骤之间的其他细节。

读完这篇文章你将会了解:

  • Aop 是什么?

  • 为什么要使用 Aop?

  • Spirng 实现 Aop 的思路是什么

  • 自己根据 Spring 思想实现一个 Aop 框架

Aop 是什么?

面向切面的程序设计(aspect-oriented programming,AOP)。通过预编译方式和运行期动态代理实现程序功能统一维护的一种技术。

为什么需要使用 Aop?

面向切面编程,实际上就是通过预编译或者动态代理技术在不修改源代码的情况下给原来的程序统一添加功能的一种技术。我们看几个关键词,第一个是“动态代理技术”,这个就是Spring Aop实现底层技术。第二个“不修改源代码”,这个就是Aop最关键的地方,也就是我们平时所说的非入侵性。。第三个“添加功能”,不改变原有的源代码,为程序添加功能。

举个例子:如果某天你需要统计若干方法的执行时间,如果不是用Aop技术,你要做的就是为每一个方法开始的时候获取一个开始时间,在方法结束的时候获取结束时间。二者之差就是方法的执行时间。如果对每一个需要统计的方法都做如上的操作,那代码简直就是灾难。如果我们使用Aop技术,在不修改代码的情况下,添加一个统计方法执行时间的切面。代码就变得十分优雅。具体这个切面怎么实现?看完下面的文章你一定就会知道。

Spring Aop 是怎么实现的?

所谓:

计算机程序 = 数据结构 + 算法

在阅读过Spring源码之后,你就会对这个说法理解更深入了。

Spring Aop实现的代码非常非常的绕。也就是说 Spring 为了灵活做了非常深层次的抽象。同时 Spring为了兼容 @AspectJ 的Aop协议,使用了很多 Adapter (适配器)模式又进一步的增加了代码的复杂程度。

Spring 的 Aop 实现主要以下几个步骤:

  1. 初始化 Aop 容器。

  2. 读取配置文件。

  3. 将配置文件装换为 Aop 能够识别的数据结构 -- Advisor 。这里展开讲一讲这个advisor。Advisor对象中包又含了两个重要的数据结构,一个是 Advice ,一个是 Pointcut Advice 的作用就是描述一个切面的行为, pointcut 描述的是切面的位置。两个数据结的组合就是”在哪里,干什么“。这样 Advisor 就包含了”在哪里干什么“的信息,就能够全面的描述切面了。

  4. Spring 将这个 Advisor 转换成自己能够识别的数据结构 -- AdvicedSupport 。Spirng 动态的将这些方法拦截器织入到对应的方法。

  5. 生成动态代理代理。

  6. 提供调用,在使用的时候,调用方调用的就是代理方法。也就是已经织入了增强方法的方法。

自己实现一个 Aop 框架

同样,我也是参考了Aop的设计。只实现了基于方法的拦截器。去除了很多的实现细节。

使用上一讲的 IoC 框架管理对象。使用 Cglib 作为动态代理的基础类。使用 maven 管理 jar 包和 module。所以上一讲的 IoC 框架会作为一个 modules 引入项目。

下面我们就来实现我们的Aop 框架吧。

首先来看看代码的基本结构。

代码结构比上一讲的 IoC 复杂不少。我们首先对包每个包都干了什么做一个简单介绍。

  • invocation 描述的就是一个方法的调用。注意这里指的是“方法的调用”,而不是调用这个动作。

  • interceptor 大家最熟悉的拦截器,拦截器拦截的目标就是 invcation 包里面的调用。

  • advisor 这个包里的对象,都是用来描述切面的数据结构。

  • adapter 这个包里面是一些适配器方法。对于"适配器"不了解的同学可以去看看"设计模式"里面的"适配模式"。他的作用就是将 advice 包里的对象适配为 interceptor

  • bean 描述我们 json 配置文件的对象。

  • core 我们框架的核心逻辑。

这个时候宏观的看我们大概梳理出了一条路线, adaper advisor 适配为 interceptor 去拦截 invoction

下面我们从这个链条的最末端讲起:

invcation

首先 MethodInvocation 作为所有方法调用的接口。要描述一个方法的调用包含三个方法,获取方法本身 getMethod ,获取方法的参数 getArguments ,还有执行方法本身 proceed ()

  1. public interface MethodInvocation {

  2.    Method getMethod();

  3.    Object[] getArguments();

  4.    Object proceed() throws Throwable;

  5. }

ProxyMethodInvocation 看名字就知道,是代理方法的调用,增加了一个获取代理的方法。

  1. public interface ProxyMethodInvocation extends MethodInvocation {

  2.    Object getProxy();

  3. }

interceptor

AopMethodInterceptor 是 Aop 容器所有拦截器都要实现的接口:

  1. public interface AopMethodInterceptor {

  2.    Object invoke(MethodInvocation mi) throws Throwable;

  3. }

同时我们实现了两种拦截器 BeforeMethodAdviceInterceptor AfterRunningAdviceInterceptor ,顾名思义前者就是在方法执行以前拦截,后者就在方法运行结束以后拦截:

  1. public class BeforeMethodAdviceInterceptor implements AopMethodInterceptor {

  2.    private BeforeMethodAdvice advice;

  3.    public BeforeMethodAdviceInterceptor(BeforeMethodAdvice advice) {

  4.        this .advice = advice;

  5.    }

  6.    @Override

  7.    public Object invoke(MethodInvocation mi) throws Throwable {

  8.        advice.before(mi.getMethod(),mi.getArguments(),mi);

  9.        return mi.proceed();

  10.    }

  11. }

  1. public class AfterRunningAdviceInterceptor implements AopMethodInterceptor {

  2.    private AfterRunningAdvice advice;

  3.    public AfterRunningAdviceInterceptor(AfterRunningAdvice advice) {

  4.        this.advice = advice;

  5.    }

  6.    @Override

  7.    public Object invoke(MethodInvocation mi) throws Throwable {

  8.        Object returnVal = mi.proceed();

  9.        advice.after(returnVal,mi.getMethod(),mi.getArguments(),mi);

  10.        return returnVal;

  11.    }

  12. }

看了上面的代码我们发现,实际上 mi . proceed () 才是执行原有的方法。而 advice 我们上文就说过,是描述增强的方法”干什么“的数据结构,所以对于这个before拦截器,我们就把advice对应的增强方法放在了真正执行的方法前面。而对于after拦截器而言,就放在了真正执行的方法后面。

这个时候我们过头来看最关键的 ReflectioveMethodeInvocation

  1. public class ReflectioveMethodeInvocation implements ProxyMethodInvocation {

  2.    public ReflectioveMethodeInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList) {

  3.        this.proxy = proxy;

  4.        this.target = target;

  5.        this.method = method;

  6.        this.arguments = arguments;

  7.        this.interceptorList = interceptorList;

  8.    }

  9.    protected final Object proxy;

  10.    protected final Object target;

  11.    protected final Method method;

  12.    protected Object[] arguments = new Object[0];

  13.    //存储所有的拦截器

  14.    protected final List<AopMethodInterceptor> interceptorList;

  15.    private int currentInterceptorIndex = -1;

  16.    @Override

  17.    public Object getProxy() {

  18.        return proxy;

  19.    }

  20.    @Override

  21.    public Method getMethod() {

  22.        return method;

  23.    }

  24.    @Override

  25.    public Object[] getArguments() {

  26.        return arguments;

  27.    }

  28.    @Override

  29.    public Object proceed() throws Throwable {

  30.         //执行完所有的拦截器后,执行目标方法

  31.        if(currentInterceptorIndex == this.interceptorList.size() - 1) {

  32.            return invokeOriginal();

  33.        }

  34.        //迭代的执行拦截器。回顾上面的讲解,我们实现的拦击都会执行 im.proceed() 实际上又会调用这个方法。实现了一个递归的调用,直到执行完所有的拦截器。

  35.        AopMethodInterceptor interceptor = interceptorList.get(++currentInterceptorIndex);

  36.        return interceptor.invoke(this);

  37.    }

  38.    protected Object invokeOriginal() throws Throwable{

  39.        return ReflectionUtils.invokeMethodUseReflection(target,method ,arguments);

  40.    }

  41. }

在实际的运用中,我们的方法很可能被多个方法的拦截器所增强。所以我们,使用了一个list来保存所有的拦截器。所以我们需要递归的去增加拦截器。当处理完了所有的拦截器之后,才会真正调用调用被增强的方法。我们可以认为,前文所述的动态的织入代码就发生在这里。

  1. public class CglibMethodInvocation extends ReflectioveMethodeInvocation {

  2.    private MethodProxy methodProxy;

  3.    public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, List<AopMethodInterceptor> interceptorList, MethodProxy methodProxy) {

  4.        super(proxy, target, method, arguments, interceptorList);

  5.        this.methodProxy = methodProxy;

  6.    }

  7.    @Override

  8.    protected Object invokeOriginal() throws Throwable {

  9.        return methodProxy.invoke(target,arguments);

  10.    }

  11. }

CglibMethodInvocation 只是重写了 invokeOriginal 方法。使用代理类来调用被增强的方法。

advisor

这个包里面都是一些描述切面的数据结构,我们讲解两个重要的。

  1. @Data

  2. public class Advisor {

  3.    //干什么

  4.    private Advice advice;

  5.    //在哪里

  6.    private Pointcut pointcut;

  7. }

如上文所说,advisor 描述了在哪里,干什么。

  1. @Data

  2. public class AdvisedSupport extends Advisor {

  3.    //目标对象

  4.    private TargetSource targetSource;

  5.    //拦截器列表

  6.    private List<AopMethodInterceptor> list = new LinkedList<>();

  7.    public void addAopMethodInterceptor(AopMethodInterceptor interceptor){

  8.        list.add(interceptor);

  9.    }

  10.    public void addAopMethodInterceptors(List< AopMethodInterceptor> interceptors){

  11.        list.addAll(interceptors);

  12.    }

  13. }

这个 AdvisedSupport 就是 我们Aop框架能够理解的数据结构,这个时候问题就变成了--对于哪个目标,增加哪些拦截器。

core

有了上面的准备,我们就开始讲解核心逻辑了。

  1. @Data

  2. public class CglibAopProxy implements AopProxy{

  3.    private AdvisedSupport advised;

  4.    private Object[] constructorArgs;

  5.    private Class>[] constructorArgTypes;

  6.    public CglibAopProxy(AdvisedSupport config){

  7.        this.advised = config;

  8.    }

  9.    @Override

  10.    public Object getProxy() {

  11.        return getProxy(null);

  12.    }

  13.    @Override

  14.    public Object getProxy(ClassLoader classLoader) {

  15.        Class> rootClass = advised.getTargetSource().getTagetClass();

  16.        if(classLoader == null){

  17.            classLoader = ClassUtils.getDefultClassLoader();

  18.        }

  19.        Enhancer enhancer = new Enhancer();

  20.        enhancer.setSuperclass (rootClass.getSuperclass());

  21.        //增加拦截器的核心方法

  22.        Callback callbacks = getCallBack(advised);

  23.        enhancer.setCallback(callbacks);

  24.        enhancer.setClassLoader(classLoader);

  25.        if(constructorArgs != null && constructorArgs.length > 0){

  26.            return enhancer.create(constructorArgTypes,constructorArgs);

  27.        }

  28.        return enhancer.create();

  29.    }

  30.    private Callback getCallBack(AdvisedSupport advised) {

  31.         return new DynamicAdvisedIcnterceptor(advised.getList(),advised.getTargetSource());

  32.    }

  33. }

CglibAopProxy 就是我们代理对象生成的核心方法。使用 cglib 生成代理类。我们可以与之前ioc框架的代码。比较发现区别就在于:

  1.    Callback callbacks = getCallBack(advised);

  2.    enhancer.setCallback(callbacks);

callback与之前不同了,而是写了一个 getCallback () 的方法,我们就来看看 getCallback 里面的 DynamicAdvisedIcnterceptor 到底干了啥。

篇幅问题,这里不会介绍 cglib 的使用,对于callback的作用,不理解的同学需要自行学习。

  1. public class DynamicAdvisedInterceptor implements MethodInterceptor{

  2.    protected final List<AopMethodInterceptor> interceptorList;

  3.    protected final TargetSource targetSource;

  4.     public DynamicAdvisedInterceptor(List<AopMethodInterceptor> interceptorList, TargetSource targetSource) {

  5.        this.interceptorList = interceptorList;

  6.        this.targetSource = targetSource;

  7.    }

  8.    @Override

  9.    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

  10.        MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);

  11.         return invocation.proceed();

  12.    }

  13. }

这里需要注意, DynamicAdvisedInterceptor 这个类实现的 MethodInterceptor 是 gclib的接口,并非我们之前的 AopMethodInterceptor。

我们近距离观察 intercept 这个方法我们看到:

  1. MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);

通过这行代码,我们的整个逻辑终于连起来了。也就是这个动态的拦截器,把我们通过 CglibMethodInvocation 织入了增强代码的方法,委托给了 cglib 来生成代理对象。

至此我们的 Aop 的核心功能就实现了。

AopBeanFactoryImpl
  1. public class AopBeanFactoryImpl extends BeanFactoryImpl{

  2.    private static final ConcurrentHashMap<String,AopBeanDefinition> aopBeanDefinitionMap = new ConcurrentHashMap<>();

  3.    private static final ConcurrentHashMap<String,Object> aopBeanMap = new ConcurrentHashMap<>();

  4.    @Override

  5.    public Object getBean(String name) throws Exception {

  6.        Object aopBean = aopBeanMap.get(name);

  7.        if(aopBean != null){

  8.            return aopBean;

  9.        }

  10.        if(aopBeanDefinitionMap.containsKey(name)){

  11.            AopBeanDefinition aopBeanDefinition = aopBeanDefinitionMap.get(name);

  12.             AdvisedSupport advisedSupport = getAdvisedSupport(aopBeanDefinition);

  13.            aopBean = new CglibAopProxy(advisedSupport).getProxy();

  14.            aopBeanMap.put(name,aopBean);

  15.            return aopBean;

  16.        }

  17.        return super.getBean(name);

  18.    }

  19.    protected void registerBean(String name, AopBeanDefinition aopBeanDefinition){

  20.        aopBeanDefinitionMap.put(name,aopBeanDefinition);

  21.    }

  22.    private AdvisedSupport getAdvisedSupport (AopBeanDefinition aopBeanDefinition) throws Exception {

  23.        AdvisedSupport advisedSupport = new AdvisedSupport();

  24.        List<String> interceptorNames = aopBeanDefinition.getInterceptorNames();

  25.        if(interceptorNames != null && !interceptorNames.isEmpty()){

  26.            for (String interceptorName : interceptorNames) {

  27.                Advice advice = (Advice) getBean(interceptorName);

  28.                Advisor advisor = new Advisor();

  29.                advisor







请到「今天看啥」查看全文