• SpringAOP(Debug查看执行流程)


    1 AOP的几个核心技术

      AOP-面向切面编程的实现的核心技术:jvm运行期间对字节码进行修改或者动态生成新的字节码文件(asm技术)。

     

    2 AOP的几个核心概念

      AOP在运行期间我们要对class文件做修改或者生成新的。AOP就定义了一套规范,包括了切面、切点、连接点、通知、织入等等这些内容。

    (1)连接点(Join point):指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。

    (2)切面(Aspect):被抽取出来的公共模块,可以用来横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。

    (3)切点(Pointcut):切点用于定义 要对哪些Join point进行拦截。切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。

    (4)通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。


    (5)目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。

    (6)织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。

    (7)引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。

     

     

    3 通知

    3.1 前置通知
    前置通知:在方法执行之前执行的通知。前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值.

    @Before("declareJoinPointExpression()")
    public void beforMethod(JoinPoint joinPoint){
        /*获取方法名*/
        String methodName = joinPoint.getSignature().getName();
        /*获取方法的参数*/
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        
        System.out.println("The method "+methodName+" begins with"+args);
    }

    外部类切入点表达式方法引用如下:
    使用类全路径+方法名

    @Before("com.web.aop.LogAspects.pointCut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
    }


    3.2 后置通知
    后置通知:在目标方法执行后执行,无论是否抛出异常。后置通知中不能访问目标方法执行后返回的结果。

    @After("execution(* com.web.aop.impl.*.*(int , int ))")
    /*表示该包下所有返回类型的所有类的所有方法*/
    public void afterMethod(JoinPoint joinPoint){
        /*获取方法名*/
        String methodName = joinPoint.getSignature().getName();
        /*获取方法的参数*/
        List<Object> args = Arrays.asList(joinPoint.getArgs());
    
        System.out.println("The method "+methodName+" ends with"+args);
        
    }


    3.3 返回通知
    返回通知 :在方法正常执行返回结果后执行,返回通知可以获取目标方法的返回值。
    在返回通知中, 只要将 returning 属性添加到 @AfterReturning 注解中, 就可以访问连接点的返回值。 该属性的值即为用来传入返回值的参数名称。
    必须在通知方法的签名中添加一个同名参数,在运行时Spring AOP 会通过这个参数传递返回值。
    原始的切点表达式需要出现在 pointcut 属性中。

    @AfterReturning(pointcut="execution(* com.web.aop.impl.*.*(int , int ))",returning="result")
    /*表示该包下所有返回类型的所有类的所有方法*/
    public void afterReturning(JoinPoint joinPoint ,Object result){
        /*获取方法名*/
        String methodName = joinPoint.getSignature().getName();
        /*获取方法的参数*/
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        
        System.out.println("The method "+methodName+" ends with "+result);
        
    }


    3.4 异常通知
    在方法抛出异常后执行,可以访问到异常对象。且可以指定在出现特定异常对象时,再执行。
    只在连接点抛出异常时才执行异常通知
     将 throwing 属性添加到 @AfterThrowing 注解中, 也可以访问连接点抛出的异常.
    Throwable 是所有错误和异常类的超类. 所以在异常通知方法可以捕获到任何错误和异常.
    如果只对某种特殊的异常类型感兴趣, 可以将参数声明为其他异常的参数类型. 然后通知就只在抛出这个类型及其子类的异常时才被执行。

    @AfterThrowing(value="execution(* com.web.aop.impl.*.*(int , int ))",throwing="exception")
    /*表示该包下所有返回类型的所有类的所有方法*/
    public void afterThrowing(JoinPoint joinPoint ,Exception exception){
        /*获取方法名*/
        String methodName = joinPoint.getSignature().getName();
        /*获取方法的参数*/
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        
        
        System.out.println("The method "+methodName+" throws exception :"+exception);
        
    }

     

    3.5 环绕通知
    环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.动态代理,手动推进目标方法运行(joinPoint.procced())
    环绕通知需要携带ProceedingJoinPoint类型的参数;
    环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法;
    环绕通知必须有返回值,返回值即为目标方法的返回值 !
    对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint 。它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点。
    在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法。 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行。
    注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed() 的返回值, 否则会出现空指针异常。

    @Around("execution(* com.web.aop.impl.*.*(int , int ))")
    /*表示该包下所有返回类型的所有类的所有方法*/
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        
        System.out.println("This is aroundMethod....");
        
        /*获取方法名*/
        String methodName = joinPoint.getSignature().getName();
        /*获取方法的参数*/
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        
        Object result = null;
        try {
            //前置通知
            System.out.println("The method "+methodName+" begins with  :"+args);
            
            result = joinPoint.proceed();
            
            //返回通知
            System.out.println();
            System.out.println("The method "+methodName+" ends with  :"+result);
        } catch (Throwable e) {
            // 异常通知
            System.out.println("The method "+methodName+" throws exception :"+e);
            throw new RuntimeException(e);
        }
        
        //后置通知
        System.out.println("The method "+methodName+" ends ");
        
        return result;    
    }

     

    3.6 切点的表达式

     

     

     

    4 AOP的执行过程

     

    具体顺序

      1)从chain取出around通知对象,执行自定义的aorund通知方法前置通知内容(Object proceed = joinPoint.proceed()之前的内容)

      2)从chain取出before通知对象,执行自定义的before通知方法

      3)从chain取出after通知对象

      4)从chain取出afterreturning通知对象

      5)从chain取出atferthrowing通知对象

      6)执行连接点的逻辑方法

      7)执行自定义的afterthrowing的通知方法(没有异常不会执行)

      8)执行自定义的afterreturning的通知方法

      9)执行自定义的afterre的通知方法

      10)继续执行自定义的around通知方法,执行后置通知内容(Object proceed = joinPoint.proceed()之后的内容)

     

    5 测试代码(配合上图看)

    5.1 切面

    @Component
    @Aspect
    public class AopLogUtils {
    
        @Pointcut("execution(public * com.ruoyi.weixin.user.AopTest.AopTestServiceImpl.test(..)))")
        public void myPointCut(){};
    
    
        @Around(value = "myPointCut()")
        public Object doAround(ProceedingJoinPoint joinPoint)
        {
            int result = 0;
            try {
                System.out.println("doAround 前面执行");
                Object proceed = joinPoint.proceed();
                System.out.println("doAround 后面执行");
            }catch (Throwable t){
                System.out.println("doAround 出错");
            }finally {
                System.out.println("doAround finally执行");
            }
            return result;
    
        }
    
        @Before(value = "myPointCut()")
        public void doBefore(JoinPoint joinPoint)
        {
            System.out.println("doBefore 执行");
        }
    
        @After(value = "myPointCut()")
        public void doAfter(JoinPoint joinPoint)
        {
            System.out.println("doAfter 执行");
        }
    
    
        @AfterReturning(value = "myPointCut()",returning = "jsonResult")
        public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
        {
            System.out.println("doAfterReturning 执行");
        }
    
    
        @AfterThrowing(value = "myPointCut()", throwing = "e")
        public void doAfterThrowing(JoinPoint joinPoint, Exception e)
        {
            System.out.println("AfterThrowing 执行");
        }
    }

     

    5.2 业务代码

    public interface AopTestService {
        void test();
    }
    @Service("aoptestservice")
    public class AopTestServiceImpl implements AopTestService{
    
        @Override
        public void test(){
            System.out.println("asdf");
        }
    
    }
    @RestController
    public class MyAopTest {
    
        @Autowired
        @Qualifier("aoptestservice")
        private AopTestService aoptestservice;
    
        @GetMapping("/lj_weixin/aoptest")
        public void test(){
    
            aoptestservice.test();
        }

     

    5.3 执行调用debug

    进入controller,我们看一下aoptestservice,发现它是一个代理对象

     

     看它的属性,里面有一个CALLBACK数组

     

    class CglibAopProxy implements AopProxy, Serializable {
        2 public static class SerializableNoOp implements NoOp, Serializable {}
        private static class StaticUnadvisedInterceptor implements MethodInterceptor, Serializable {}
        1 private static class StaticUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {}
        private static class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable {}
        private static class DynamicUnadvisedExposedInterceptor implements MethodInterceptor, Serializable {}
        3 private static class StaticDispatcher implements Dispatcher, Serializable {}
        4 private static class AdvisedDispatcher implements Dispatcher, Serializable {}
        5 private static class EqualsInterceptor implements MethodInterceptor, Serializable {}
        6 private static class HashCodeInterceptor implements MethodInterceptor, Serializable {}
        private static class FixedChainStaticTargetInterceptor implements MethodInterceptor, Serializable {}
        0 private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {}
        private static class CglibMethodInvocation extends ReflectiveMethodInvocation {}
        private static class ProxyCallbackFilter implements CallbackFilter {}
    }

     

    6 f7进入方法来到CglibAopProxy.java里面的内部类DynamicAdvisedInterceptor的intercept方法

      和上面的CALLBACK数组的元素比对,发现它的0号元素就是DynamicAdvisedInterceptor

     

     往下执行,一直到下面代码执行完

    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);          

    chain是一个集合,里面存储了通知对象,我们可以看到,一共有6个元素。

    但是我们实际上只定义了5个通知,多了一个元素0号元素:ExposeInvocationInterceptor

    1-5号元素就是我们定义的通知:它们的顺序是around-before-after-afterreturn-afterthrowing

     

    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

     chain放入CglibMethodInvocation对象中,调用proceed()方法

     

    7 进入CglibAopProxy.java.CglibMethodInvocation.java的proceed方法

    private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
    public Object proceed() throws Throwable {
                try {
                    return super.proceed();
                }

     

    8 进入super.proceed,也就是ReflectiveMethodInvocation.java的proceed方法

    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
    currentInterceptorIndex = -1 ,this.interceptorsAndDynamicMethodMatchers.size() = 6

    Object interceptorOrInterceptionAdvice =
                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    获取到的interceptorOrInterceptionAdvice是ExposeInvocationInterceptor对象,也就是chain里面的0好元素

    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

    里面传入的this是上面创建的CglibMethodInvocation对象(它里面有chain对象)

     

     9 进入ExposeInvocationInterceptor的invoke方法

    @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            MethodInvocation oldInvocation = invocation.get();
            invocation.set(mi);
            try {
                return mi.proceed();
            }
            finally {
                invocation.set(oldInvocation);
            }
        }

     把mi对象(也就是上面传入的this对象-CglibAopProxy.java.CglibMethodInvocation对象)赋值给invocation

    invocation是当前类ExposeInvocationInterceptor的一个static final静态变量
    private static final ThreadLocal<MethodInvocation> invocation =
                new NamedThreadLocal<>("Current AOP method invocation");

     

    10 然后调用mi的proceed方法,也就是CglibAopProxy.java.CglibMethodInvocation.java的proceed方法

    进入proceed方法

    public Object proceed() throws Throwable {
                try {
                    return super.proceed();
                }

     

    11 调用super.proceed方法,又回到了ReflectiveMethodInvocation的proceed方法

    此时currentInterceptorIndex值为0

     

     

    get(++this.currentInterceptorIndex)取出来的是AspectAroundAdvice 环绕通知
    Object interceptorOrInterceptionAdvice =
                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

     

    12 执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);进入AspectJAroundAdvice.java的invoke方法

     进入invokeAdviceMethod方法

    进入invokeAdviceMethodWithGivenArgs方法

     进入this.aspectJAdviceMethod.invoke方法,也就是Method.java的invoke方法

    执行ma.invoke(obj, args);

    执行,来到环绕通知方法

    执行完System.out.println("doAround 前面执行");这个就是around的第一次执行

     

     13 接着执行我们定义的环绕通知方法里面的代码Object proceed = joinPoint.proceed();进入MethodInvocationProceedingJoinPoint.java的proceed方法

     

    14 进入this.methodInvocation.invocableClone().proceed()也就是CglibAopProcy.java.CglibMethodInvocation.java.proceed方法(又回到这个方法)

     

    15 (同11步)进入super.proceed,也就是ReflectiveMethodInvocation.java的proceed方法

    此时currentInterceptorIndex为1

    取出++this.currentInterceptorIndex号元素,为MethodBeforeAdviceInterceptor,也就是before通知

    Object interceptorOrInterceptionAdvice =
                    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

     

     16 执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);进入MethodBeforeAdviceInterceptor.java的invoke方法

    执行完this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());

    before通知方法已执行

     

     17 (同11步)调用super.proceed方法,又回到了ReflectiveMethodInvocation的proceed方法

    此时currentInterceptorIndex为2

     取出++this.currentInterceptorIndex号元素,为AspectJAfterAdvice,也就是after通知

     

    18 执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);进入AspectJAfterAdvice.java的invoke方法

    直接执行return mi.proceed();并没有去调用我们定义的after通知的方法

     

     

    19 (同11步)调用super.proceed方法,又回到了ReflectiveMethodInvocation的proceed方法

    此时currentInterceptorIndex为2

      取出++this.currentInterceptorIndex号元素,为AfterReturningAdviceInterceptor,也就是afterReturning通知

     

    20 执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);进入AfterReturningAdviceInterceptor.java的invoke方法

      先直接执行Object retVal = mi.proceed();等它执行完成,才去执行this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());也就是我们自定义的通知方法

     

    21 (同11步)调用super.proceed方法,又回到了ReflectiveMethodInvocation的proceed方法

    此时currentInterceptorIndex为4

      取出++this.currentInterceptorIndex号元素,为AspectAfterThrowingAdvice,也就是afterrThrowing通知

     

    22 执行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);进入AspectJAfterThrowingAdvice.java的invoke方法

    直接执行mi.proceed();执行完成后,采取执行invokeAdviceMethod(getJoinPointMatch(), null, ex);也就是我们自定义的通知方法

     

     23 进入super.proceed,也就是ReflectiveMethodInvocation.java的proceed方法

    此时currentInterceptorIndex为5,判断为true,执行return invokeJoinpoint();

     

    24 执行return invokeJoinpoint();进入进入CglibAopProxy.java.CglibMethodInvocation.java的invokeJoinpoint方法

     执行this.methodProxy.invoke(this.target, this.arguments);来到MethodProxy的invoke方法

     

     

    25 执行 fci.f1.invoke(fci.i1, obj, args);来到我们的连接点方法

     

    26 一步步执行,回到AspectJAfterThrowingAdvice.invoke方法

    回到ThrowingAdvice通知的invoke方法,继续执行,由于没有异常,不会执行catch里面的invokeAdviceMethod(getJoinPointMatch(), null, ex);,所以自定义的通知方法不会执行

     

    27 继续执行,回到AfterReturningAdviceInterceptor的invoke方法

    回到了AfterReturning通知的invoke方法

    执行this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());

    一步步往里面走,直到执行我们自定义的通知方法

     

     28 继续执行,回到AspectJAfterAdvice的invoke方法

    执行invokeAdviceMethod(getJoinPointMatch(), null, null);

    一步步往里面走,直到执行我们自定义的通知方法

     

    29 继续走,回到自定义的around通知方法

     执行完

    到此,五个通知全部执行完成

     

  • 相关阅读:
    js插入html
    $.ajax()的使用
    [ORGINAL]datePicker OPP style based on web. for desktop applications.
    [orginal] OOP tab control based on web !!
    [ORGINAL]OPP spin box ( numeric up down control) Design based on web!!
    [orginal] slide bar based on web , from ABC
    [orginal] OOP treeView based on web.
    [ORGINAL]OOP Panel control design(based on web )
    Autofocus
    [orginal] OOP group Box control based on WEB.!!!
  • 原文地址:https://www.cnblogs.com/jthr/p/15940527.html
Copyright © 2020-2023  润新知