Spring-12 深入理解 AOP 底层 API

213 阅读2分钟

spring-logo-0.jpg

Spring-12 深入理解 AOP 底层 API

Spring 源码系列文章会遵循由浅入深,由易到难,由宏观到微观的原则,目标是尽量降低学习难度,而不是一上来就迷失在源码当中. 文章会从一个场景作为出发点,针对性的目的性极强的针对该场景对 Spring 的实现原理,源码进行探究学习。该系列文章会让你收获什么? 从对 Spring 的使用者成为 Spring 专家。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。

本章会直接使用 spring AOP 的底层 API ,帮助读者更加直观深入的理解 AOP 的执行原理。废话少说上代码。

public class AOPTest {

    static class Aspect {

        @Before("execution(* foo())")
        public void before1() {
            System.out.println("@Aspect @Before1 ------------------------------");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("@Aspect @Before2 ------------------------------");
        }

        @After("execution(* foo())")
        public void after() {
            System.out.println("@Aspect @After -------------------------------");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning() {
            System.out.println("@Aspect @AfterReturning -------------------------------");
        }

        @AfterThrowing("execution(* foo())")
        public void afterThrowing() {
            System.out.println("@Aspect @AfterThrowing -------------------------------");
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("@Aspect @Around before -------------------------------");

            Object val = pjp.proceed();
            System.out.println("@Aspect @Around after -------------------------------");
            return val;
        }
    }

    static class Target {
        public void foo() {
            System.out.println("Target foo");
        }
    }

    public static void main(String[] args) throws Throwable {

        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());

        List<Advisor> advisors = new ArrayList<>();
        for (Method method : Aspect.class.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Before.class)) {
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                Advice advice = new AspectJMethodBeforeAdvice(method , pointcut , factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut , advice);
                advisors.add(advisor);
            } else if (method.isAnnotationPresent(After.class)) {
                String expression = method.getAnnotation(After.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                Advice advice = new AspectJAfterAdvice(method , pointcut , factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut , advice);
                advisors.add(advisor);
            } else if (method.isAnnotationPresent(AfterReturning.class)) {
                String expression = method.getAnnotation(AfterReturning.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                Advice advice = new AspectJAfterReturningAdvice(method , pointcut , factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut , advice);
                advisors.add(advisor);
            } else if (method.isAnnotationPresent(AfterThrowing.class)) {
                String expression = method.getAnnotation(AfterThrowing.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                Advice advice = new AspectJAfterThrowingAdvice(method , pointcut , factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut , advice);
                advisors.add(advisor);
            } else if (method.isAnnotationPresent(Around.class)) {
                String expression = method.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                Advice advice = new AspectJAroundAdvice(method , pointcut , factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut , advice);
                advisors.add(advisor);
            }
        }

        advisors.add(ExposeInvocationInterceptor.ADVISOR);
        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }

        AnnotationAwareOrderComparator.sort(advisors);
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetClass(Target.class);
        Target target = new Target();
        proxyFactory.setTargetSource(new SingletonTargetSource(target));


        List<Object> chain = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getDeclaredMethod("foo"), Target.class);
        for (Object c : chain) {
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>> " + c);
        }

        Target proxy = (Target) proxyFactory.getProxy();
        // proxy.foo();

        Constructor<ReflectiveMethodInvocation> constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
        constructor.setAccessible(true);
        ReflectiveMethodInvocation invocation = constructor.newInstance(proxy , target ,
                Target.class.getDeclaredMethod("foo") , new Object[]{} , Target.class , chain);
        invocation.proceed();
    }
}
控制台输出
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.after()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.afterReturning()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterThrowingAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.afterThrowing()]; aspect name '']
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.interceptor.ExposeInvocationInterceptor@56a6d5a6
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.aspectj.AspectJAfterAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.after()]; aspect name ''
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@18ce0030
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@4445629
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@45b9a632
>>>>>>>>>>>>>>>>>>>>>>>>> org.springframework.aop.aspectj.AspectJAfterThrowingAdvice: advice method [public void org.devx.spring.certified.professional.edu.course.a18.A18$Aspect.afterThrowing()]; aspect name ''
@Aspect @Before1 ------------------------------
@Aspect @Before2 ------------------------------
@Aspect @Around before -------------------------------
Target foo
@Aspect @AfterReturning -------------------------------
@Aspect @Around after -------------------------------
@Aspect @After -------------------------------

我们完全使用 spring 的底层 API 完成了 AOP 功能。通过 ReflectiveMethodInvocation 进行了方法的调用,这里如果使用 proxy.foo() 其结果是一样的。

Advic 转换为 MethodInterceptor

在代理对象调用方法执行过程中所有的 Advic 都要被转换为 MethodInterceptor 这样的环绕形式才能工作。

@Before 注解对应的 MethodBeforeAdvice 会被转换为 MethodBeforeAdviceInterceptor。

image.png

@After 注解对应的 AspectJAfterAdvice 自身实现了 MethodInterceptor 接口所以不需要经过转换。

image.png

@Around 注解对应的 AspectJAroundAdvice 自身实现了 MethodInterceptor 接口所以不需要经过转换。

image.png

@AfterReturning 注解对应的 AfterReturningAdvice 会被转换为 AfterReturningAdviceInterceptor。

image.png

@AfterThrowing 注解对应的 AspectJAfterThrowingAdvice 自身实现了 MethodInterceptor 接口所以不需要经过转换。

image.png

转换工作是由 AdvisorAdapter 完成,例如 MethodBeforeAdviceAdapter 将 MethodBeforeAdvice 转换为 MethodBeforeAdviceInterceptor。

image.png

image.png

ReflectiveMethodInvocation 调用链

巧妙的使用了 责任链设计模式,理解了这个就很容易理解代理对象的执行过程。如果读者仔细阅读了前面内容就会发现所有的 MethodInterceptor 实现中在 invoke 方法中都去调用了 MethodInvocation 的 proceed 方法。

image.png

代理对象内部封装的简要示意图,也可以把它理解为看源码的一个顺序或入口.

image.png


DevX 会持续分享 Java 技术干货,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。

DevX 不止于技术