AOP拦截器链编排

875 阅读3分钟

1.背景

首先聊一下,AOP各种通知执行顺序。

aop里面的四种通知类型

  1. 前置通知
  2. 后置通知
  3. 返回通知
  4. 异常通知

执行顺序

try {
    // 执行前置通知
    // 执行目标方法
    // 执行返回通知
}
catch (Exception e) {
    // 执行异常通知
}
finally {
    // 执行后置通知
}

我们知道,AOP的底层原理其实是通过动态代理技术,拦截需要代理方法执行的。那么比较容易实现的方式就是将各种通知串成一条链条,将目标方法 + 各种通知串起来执行。

2. CODING

通知方法顶层设计

/**
 * 各种通知拦截器
 */
public interface MethodInterceptor {

    Object invoke(MethodInterceptorChain methodInterceptorChain);

    void doInterceptor();
}

拦截器链

串联目标方法 + 各种通知方法,也可以说是责任链执行的上下文

/**
 * 组织 目标方法 + 各种通知方法
 */
@Data
public class MethodInterceptorChain {

    /**
     * 各种通知方法
     */
    final List<MethodInterceptor> methodInterceptors;

    /**
     * 目前处理到的通知方法 index
     */
    int currentIndex;

    /**
     * 目标对象
     */
    Object target;

    /**
     * 目标方法
     */
    Method targetMethod;

    /**
     * 目标方法执行参数
     */
    Object[] methodArgs;

    private MethodInterceptorChain() {
        currentIndex = -1;
        methodInterceptors = new ArrayList<>();
    }

    public Object process() {
        // 所有的通知方法已经执行完成 该执行
        if (currentIndex == methodInterceptors.size() - 1) {
            // 执行目标方法
            try {
                return targetMethod.invoke(target, methodArgs);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }

        return methodInterceptors.get(++currentIndex).invoke(this);
    }


    public static MethodInterceptorChain from(
        Object target,
        Method targetMethod,
        Object[] methodArgs,
        MethodInterceptor... interceptors
    ) {
        unValidParam(target == null, "目标对象不能为null");
        unValidParam(targetMethod == null, "目标方法不能为null");

        MethodInterceptorChain chain = new MethodInterceptorChain();
        chain.setTarget(target);
        targetMethod.setAccessible(true);
        chain.setTargetMethod(targetMethod);
        chain.setMethodArgs(methodArgs);
        ofNullable(interceptors).ifPresent(is -> {
            for (MethodInterceptor interceptor : is) {
                chain.getMethodInterceptors().add(interceptor);
            }
        });
        return chain;
    }


}

前置通知 + 后置通知 + 返回通知 + 异常通知

public class BeforeMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInterceptorChain methodInterceptorChain) {
        doInterceptor();
        return methodInterceptorChain.process();
    }

    @Override
    public void doInterceptor() {
        // 前置通知
        System.out.println("前置通知");
    }
}


public class AfterMethodInterceptor implements MethodInterceptor {


    @Override
    public Object invoke(MethodInterceptorChain methodInterceptorChain) {
        try {
            return methodInterceptorChain.process();
        } finally {
            doInterceptor();
        }
    }

    @Override
    public void doInterceptor() {
        System.out.println("执行后置通知");
    }
}


public class AfterReturnMethodInterceptor implements MethodInterceptor{

    Object rVal;

    @Override
    public Object invoke(MethodInterceptorChain methodInterceptorChain) {
        rVal = methodInterceptorChain.process();
        doInterceptor();
        return rVal;
    }

    @Override
    public void doInterceptor() {
        System.out.println("执行返回通知");
    }
}


public class AfterThrowingMethodInterceptor implements MethodInterceptor {

    Exception e;

    @Override
    public Object invoke(MethodInterceptorChain methodInterceptorChain) {
        try {
            return methodInterceptorChain.process();
        } catch (Exception e) {
            this.e = e;
            doInterceptor();
            throw e;
        }
    }

    @Override
    public void doInterceptor() {
        System.out.println("执行异常通知, " + e.getClass());
    }
}

我们可以看到的是不同类型的通知,他们的invoke逻辑不太一样,这里也是说明了为什么后置通知一定会执行,返回通知在出现异常的情况下不会执行,异常通知只有出现异常的时候才会执行,需要去体会一下这个代码设计。

测试类

public class TestService {
    public String test() {
        throw new IllegalArgumentException("11");
    }
}
public static void main(String[] args) throws NoSuchMethodException {
    TestService testService = new TestService();
    Method method = testService.getClass().getDeclaredMethod("test");
    MethodInterceptor beforeMethodInterceptor = new BeforeMethodInterceptor();
    MethodInterceptor afterReturnMethodInterceptor = new AfterReturnMethodInterceptor();
    MethodInterceptor afterMethodInterceptor = new AfterMethodInterceptor();
    MethodInterceptor afterThrowingMethodInterceptor = new AfterThrowingMethodInterceptor();
    MethodInterceptorChain chain = MethodInterceptorChain.from(
        testService,
        method,
        null,
        beforeMethodInterceptor,
        afterMethodInterceptor,
        afterReturnMethodInterceptor,
        afterThrowingMethodInterceptor
    );
    System.out.println(chain.process());
}

正常执行

前置通知
执行返回通知
执行后置通知
hello world

出现异常

前置通知
执行异常通知, class java.lang.RuntimeException
执行后置通知
Exception in thread "main" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:57)
	at com.hdu.interceptor.AfterThrowingMethodInterceptor.invoke(AfterThrowingMethodInterceptor.java:12)
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:61)
	at com.hdu.interceptor.AfterReturnMethodInterceptor.invoke(AfterReturnMethodInterceptor.java:12)
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:61)
	at com.hdu.interceptor.AfterMethodInterceptor.invoke(AfterMethodInterceptor.java:11)
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:61)
	at com.hdu.interceptor.BeforeMethodInterceptor.invoke(BeforeMethodInterceptor.java:10)
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:61)
	at com.hdu.TestMain.main(TestMain.java:28)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.hdu.chain.MethodInterceptorChain.process(MethodInterceptorChain.java:55)
	... 9 more
Caused by: java.lang.IllegalArgumentException: 11
	at com.hdu.service.TestService.test(TestService.java:7)
	... 14 more

3. 总结

上面聊了一下 AOP 的责任链是怎么设计的。

下面说一下AOP的原理大概就是

准备阶段:

  1. 扫描所有切面类。
  2. 将切面类的方法提取,构成通知方法。

BeanPostProcessor阶段:

  1. 根据已有的切面类,使用切点进行匹配
  2. 如果匹配上了,那么寻找该对象能够匹配上的通知方法
  3. 为该对象生成一个代理类,代理类的方法拦截里面含有各种通知方法构成的责任链