Spring-12 深入理解 AOP 底层 API

101 阅读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 完成,例如 MethodBeforeAdviceAdapterMethodBeforeAdvice 转换为 MethodBeforeAdviceInterceptor

image.png

image.png

ReflectiveMethodInvocation 调用链

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

image.png

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

image.png


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

DevX 不止于技术