手写SpringAOP

一、非注解式简易版AOP

整体流程
在这里插入图片描述

1.1 代码

public class Test {
    public static void main(String[] args){
        // Aop代理工厂
        DefaultAopProxyFactory factory = new DefaultAopProxyFactory();
        // 测试对象
        AOPDemoImpl demo = new AOPDemoImpl();
        // 支撑类:用于存放目标对象、各种通知以及目标对象的接口
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTarget(demo);
        // 生成三个通知类,分别代表环绕通知,前置通知和后置通知
        Advice logAdvice = new LogAdvice();
        Advice beforeAdvice = new BeforeAdvice();
        Advice afterAdvice = new AfterAdvice();
        // 通知器:将切入点和通知连接起来,是对切面的实现
        Advisor advisor1 = new DefaultPointcutAdvisor(null, logAdvice, "0");
        Advisor advisor2 = new DefaultPointcutAdvisor(null, beforeAdvice, "1");
        Advisor advisor3 = new DefaultPointcutAdvisor(null, afterAdvice, "2");
        advisedSupport.addAroundAdvisors(advisor1);
        advisedSupport.addBeforeAdvisors(advisor2);
        advisedSupport.addAfterAdvisors(advisor3);
        Class<?>[] interfaces = demo.getClass().getInterfaces();
        advisedSupport.setInterfaces(interfaces);
        // 构建代理对象
        AopProxy aopProxy = factory.createAopProxy(advisedSupport);
        AOPDemo proxyObj = (AOPDemo) aopProxy.getProxy();
        // 执行方法
        proxyObj.send();
    }
}

这种AOP实现方式,没有借助注解,并且也不像Spring官方代码中,通知类型可以细化到方法上。

可以看出目前实现方式还是在类的层面,每一个类被定义为是环绕通知还是前置等。

此外,也没有涉及到切入点的实现,不过通过这个简易版AOP,可以帮助我们更好的理解AOP的实现原理。

1.2 关键类和接口

  1. 通知链:一个或多个List;
  2. Advice:通知,表示增强的功能;
  3. MethodInvocation:方法调用,用来对通知链中的通知进行依次调用;
  4. MethodInterceptor:方法拦截器,被MethodInvocation调用后,执行对应的通知,之后再调用回MethodInvocation
1.2.1 通知链

其实本质就是一个List,包括前置通知链、环绕通知链和后置通知链。

通知链中存放的是一个一个的通知类。

执行顺序为:前环绕通知—>前置通知—>目标方法—>后置通知—>后环绕通知。

实现的难点:怎么将环绕通知分开呢?前置/后置我们可以根据方法放置在目标方法的前面或后面来实现。对于环绕通知是一个方法,我们怎么将内部的代码分成两部分呢?

要实现这个需求,我们应该使用递归。当我们检测到要执行环绕通知后,会在进入到环绕通知内部执行后续所有操作,直到全部通知执行完成,才会从环绕通知中出来。

在这里插入图片描述

1.2.2 Advice

在这里插入图片描述

Advice:通知,表示增强的功能。我们根据情况定义了三个子接口。

实现这些接口的类,就是通知类。

值得注意的是:beforeafter都是无参的方法,而around需要传递一个ProceedingJoinPoint对象。

环绕通知比较特殊,它需要先执行前一部分通知,再执行目标方法(或before–>target),最后执行后一部分通知
基于此,我们应该在环绕通知内部进行通知链调用。

其实ProceedingJoinPoint正是MethodInvocation的封装。这一点,我们后面会讲到。

pjp.proceed()内部执行的是通知链调用,会执行后续的通知,等全部执行完毕,就会回调执行后环绕。

public class BeforeAdvice implements MethodBeforeAdvice {
 @Override
 public void before() throws Throwable {
     System.out.println("-----before-----");
 }
}
public class LogAdvice implements MethodAroundAdvice {
 @Override
 public Object around(ProceedingJoinPoint pjp) throws Throwable {
     System.out.println("-----Around Start-----");
     Object ret = pjp.proceed();
     System.out.println("-----Around End-----");
     return ret;
 }
}
public class AfterAdvice implements MethodAfterAdvice {
 @Override
 public void after() throws Throwable {
     System.out.println("-----after-----");
 }
}
1.2.3 MethodInvocation

上一节提到了MethodInvocaiton,它是继承Joinpoint的一个子接口。Joinpoint是连接点,可以看作目标对象的全部方法。

它提供了四个方法:

  1. getThis获取Joinpoint代表方法所属对象;
  2. getArgs获取Joinpoint代表方法的参数;
  3. getMethod获取所代表的方法;
  4. proceed执行所代表的方法。

MethodInvocation的含义是:方法调用,它的实现类ReflectiveMethodInvocation,是AOP的核心,用来完成通知的调用(通知也是方法)。

具体来说proceed内部会实现依据通知链顺序完成对每一个通知的调用,通知的具体执行交给下一节的MethodInterceptor实现。

1.2.1图,MethodInvocation的proceed方法完成的是,通知类的拿取过程,如绿蓝红色线过程。

MethodInterceptor完成的是下部分的执行过程。两者是交错进行的相互调用

在这里插入图片描述

1.2.4 MethodInterceptor

拦截器,表示对执行目标方法的流程进行拦截后,做一些自己的事情,然后再放回去执行正常流程。
在这里插入图片描述
它的invokeMethodInvocationproceed方法是交替执行的,相互调用,这一点从下面的代码中也能看出。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        this.advice=advice;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        advice.before();
        // invocation.proceed() 这个方法表示继续调用通知链中的下一个
        return invocation.proceed();
    }
}
public class MethodAroundAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodAroundAdvice advice;

    public MethodAroundAdviceInterceptor(MethodAroundAdvice advice) {
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return advice.around(new ProceedingJoinPoint(invocation));
    }
}

public class MethodAfterAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodAfterAdvice advice;
    public MethodAfterAdviceInterceptor(MethodAfterAdvice advice) {
        this.advice=advice;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        advice.after();
        // invocation.proceed() 这个方法表示继续调用通知链中的下一个
        return invocation.proceed();
    }
}
1.2.5 MethodAdviceAdapter

方法装饰器。作用就是将通知转为对应的拦截器,拦截器内部包含通知,拦截下来后,执行通知。

public class MethodAdviceAdapter implements AdvisorAdapter {
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        Advice advice = advisor.getAdivce();
        if(advice instanceof MethodBeforeAdvice){
            return new MethodBeforeAdviceInterceptor((MethodBeforeAdvice)advice);
        } else if (advice instanceof MethodAroundAdvice) {
            return new MethodAroundAdviceInterceptor((MethodAroundAdvice)advice);
        }else{
            return new MethodAfterAdviceInterceptor((MethodAfterAdvice)advice);
        }
    }
}
1.2.6 ProceedingJoinPoint

它的作用其实就是对MethodInvocation对象进行封装,完成对环绕通知的调用。这里不封装也可以,直接向环绕通知传递MethodInvocation

public class ProceedingJoinPoint {
    private MethodInvocation invocation;
    public ProceedingJoinPoint(MethodInvocation invocation) {
        this.invocation = invocation;
    }
    public Object proceed() throws Throwable {
        return invocation.proceed();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流星子弹弹堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值