• 循序渐进学习AOP切面编程(包含详细demo)


    前言

    AOP切面编程5种注解@Before、@After、@AfterRunning、@AfterThrowing、@Around
    如何匹配pointcut?
    语法规则是怎样的?

    环境说明

    springboot版本2.7.1
    jdk版本1.8
    需要使用到的依赖(可能不完整)

            
            <dependency>
                <groupId>org.aspectjgroupId>
                <artifactId>aspectjweaverartifactId>
            dependency>
            
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>fastjsonartifactId>
                <version>1.2.83_noneautotypeversion>
            dependency>
            
            <dependency>
                <groupId>commons-codecgroupId>
                <artifactId>commons-codecartifactId>
                <version>1.15version>
            dependency>
            <dependency>
                <groupId>commons-langgroupId>
                <artifactId>commons-langartifactId>
                <version>2.6version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    第一节 Advice通知注解

    1. 被拦截的controller示例

    这里我们拦截所有的controller,为了方便演示,给定一个controller

    package com.it2.springbootmybatisplus.controller;
    
    import com.alibaba.fastjson.JSON;
    import com.it2.springbootmybatisplus.pojo.User;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/user")
    @Slf4j
    public class UserController {
        @PostMapping("/save")
        public String save(User user) {
            log.info("save方法打印内容:" + JSON.toJSONString(user));
            if (user.getName().equals("xiaomi")) {//如果name等于xiaomi,则制造一个异常
                int a = 1 / 0;
            }
            return "ok";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    用到的User

    package com.it2.springbootmybatisplus.pojo;
    import lombok.Data;
    @Data
    public class User {
    
        private Integer id;
    
        private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. Advice通知注解

    package com.it2.springbootmybatisplus.config;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    
    @Component
    @Aspect
    @Slf4j
    public class MyControllerAspect {
    
        /**
         * 设置AOP切点
         */
        @Pointcut("execution(* com.it2.springbootmybatisplus.controller.*.*(..))")
        public void mypointcut() {
        }
    
        @Before(value = "mypointcut()")
        public void beforeTest(JoinPoint point) {
            log.info("-----------@Before------------");
            String methodName = point.getSignature().getName();
            List<Object> args = Arrays.asList(point.getArgs());
            System.out.println("Before的一些操作:" + methodName + ",参数为:" + args);
            log.info("-----------@Before------------");
        }
    
        /**
         * 执行方法环绕
         *
         * @param point
         */
        @Around(value = "mypointcut()")
        public Object aroundTest(ProceedingJoinPoint point) throws Throwable {
            log.info("-----------@around------------");
            String methodName = point.getSignature().getName();
            List<Object> args = Arrays.asList(point.getArgs());
            System.out.println("around的一些操作:" + methodName + ",参数为:" + args);
            /**
             * 可以获取请求的参数,实现@Cacheable缓存效果
             */
            /**
             * 打印输入的参数,这里实现效果,如果内容里包含xiaowang,则直接返回fail
             */
            Object[] objects = point.getArgs();
            for (int i = 0; i < objects.length; i++) {
                Object obj = objects[i];
                System.out.println("type:" + obj.getClass().getSimpleName() + ",   value:" + obj);
                if(obj.toString().contains("xiaowang")){//直接拦截,如果等于xiaowang,直接返回失败
                    return "fail";
                }
            }
    
            log.info("-----------@around------------");
            return point.proceed();
        }
    
    
        @After(value = "mypointcut()")
        public void afterTest(JoinPoint point) {
            log.info("-----------@After------------");
            String methodName = point.getSignature().getName();
            List<Object> args = Arrays.asList(point.getArgs());
            System.out.println("After的一些操作:" + methodName + ",参数为:" + args);
            log.info("-----------@After------------");
        }
    
        /**
         * 设置切入点不单独写
         *
         * @param point
         * @param result
         */
        @AfterReturning(value = "execution(* com.it2.springbootmybatisplus.controller.*.*(..))", returning = "result")
        public void afterReturningTest(JoinPoint point, Object result) {
            log.info("-----------@afterReturning------------");
            String methodName = point.getSignature().getName();
            List<Object> args = Arrays.asList(point.getArgs());
            System.out.println("afterReturning的一些操作:" + methodName + ",参数为:" + args);
            System.out.println("afterReturning result:" + result);
            log.info("-----------@afterReturning------------");
        }
    
    
        /**
         * 发生异常才会执行,否则不会执行
         *
         * @param point
         * @param exception
         */
        @AfterThrowing(value = "mypointcut()", throwing = "exception")
        public void afterThrowingTest(JoinPoint point, Exception exception) {
            log.info("-----------@afterThrowing------------");
            String methodName = point.getSignature().getName();
            List<Object> args = Arrays.asList(point.getArgs());
            System.out.println("afterThrowing:" + methodName + ",参数为:" + args);
            System.out.println("afterThrowing exception:" + exception);
            log.info("-----------@afterThrowing------------");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    3. 测试例子

    • 正常的测试 name=xiaoliu
      在这里插入图片描述

    通过正常的示例,我们可以发现执行顺序是@Around >> @Before >> 方法体 >> @AfterReturning >> @After
    在这里插入图片描述

    • 被拦截的测试 name=xiaowang
      可以观察到,它只执行了@Around,并且在Around中被拦截,通过这个思想,我们可以实现@Cacheable
      在这里插入图片描述
    • 发生异常的例子 name=xiaomi
      通过异常的示例,我们可以发现执行顺序是@Around >> @Before >> 方法体 >> @AfterThrowing >> @After,对比正常例子中,方法体发生异常时,@AfterThrowing会被触发;而@AfterReturning只有方法体没有抛出异常时,才会正常触发。
      在这里插入图片描述

    4. 切入时机解释

    @Before: 前置通知, 在方法执行之前执行
    @After: 后置通知, 在方法执行之后执行 。
    @AfterRunning: 返回通知, 在方法返回结果之后执行
    @AfterThrowing: 异常通知, 在方法抛出异常之后
    @Around: 环绕通知, 围绕着方法执行

    到这里我们已经掌握了5种Advice的切入时机,可以简单的进行切入式开发。


    第二节 SpringAOP表达式

    前面的demo我们已经知道了切入时机的5种Advice,那么它具体还有什么切入规则?语法是什么?

    1. SpringAOP的组成

    SpringAOP 表达式分为三种:
    指示器(Designators)
    通配符(WildCards)
    运算符(Operators)

    2. 指示器(Designators)

    前面的demo里面,我们使用到的指示器是execution,那么execution是什么?还有哪些指示器?怎么使用?
    在这里插入图片描述

    2.1 匹配方法

    2.1.1 execution()

    前面的demo中已经使用过了,直接在方法执行时切入,不需要在方法体上做任何手脚。
    execution表达式的格式

    {
        modidier-pattern?修饰符
        ret-type-pattern 返回值
        declaring-type-pattern?声明的包名
        name-pattern(param-pattern)声明的方法名(参数名)
        throws-pattern?声明的抛出的异常
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面带有?的表示可以省略,其它部分则必须填写
    示例

    #匹配 xxx.controller下面的内容,方法参数不限制
    @Pointcut("execution(* com.it2.springbootmybatisplus.controller.*.*(..))")
    
    #匹配返回值为int,修饰符为public,并且参数为(int,int)的方法
    @Pointcut("execution(public int com.it2.springbootmybatisplus.controller.*.*(int,int))")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果想要拦截符合条件,但是会抛出某些异常的方法,则需要在表达式后添加throws 要抛出的异常即可。

    2.2 匹配注解

    2.2.1 @annotation

    demo需求说明,给方法添加注解,使其打印执行耗时。

    package com.it2.springbootmybatisplus.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * 统计执行时长
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ExecutionTime {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    package com.it2.springbootmybatisplus.annotation;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    /**
     * 统计执行时长
     */
    @Component
    @Aspect
    @Slf4j
    public class ExecutionTimeAspect {
        /**
         * 设置切入点,拦截注解
         */
        @Pointcut("@annotation(com.it2.springbootmybatisplus.annotation.ExecutionTime)")
        public void mypointcut() {
        }
    
        @Around("mypointcut()")
        public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
            try {
                Signature signature = proceedingJoinPoint.getSignature();
                String className = proceedingJoinPoint.getTarget().getClass().getName();
                String methodName = signature.getName();
                long s=System.currentTimeMillis();
                Object object = proceedingJoinPoint.proceed();
                long e=System.currentTimeMillis();
                StringBuffer sb=new StringBuffer();
                sb.append(className).append(".");
                sb.append(methodName).append("()");
                log.info("方法名:{},执行时间{}毫秒",sb.toString(),(e-s));
                return object;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                throw new RuntimeException("发生错误");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    给方法添加这个注解,并启动服务器测试
    在这里插入图片描述

    2.2.2 @within

    示例

    package com.it2.springbootmybatisplus.annotation;
    import java.lang.annotation.*;
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.CLASS)
    @Documented
    public @interface MyWithin {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    package com.it2.springbootmybatisplus.annotation;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    
    @Component
    @Aspect
    @Slf4j
    public class MyWithinAspect {
    
        @Around("@within(com.it2.springbootmybatisplus.annotation.MyWithin)")
        public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
            try {
                Signature signature = proceedingJoinPoint.getSignature();
                String className = proceedingJoinPoint.getTarget().getClass().getName();
                String methodName = signature.getName();
                long s=System.currentTimeMillis();
                Object object = proceedingJoinPoint.proceed();
                long e=System.currentTimeMillis();
                StringBuffer sb=new StringBuffer();
                sb.append(className).append(".");
                sb.append(methodName).append("()");
                log.info("within--方法名:{},执行时间{}毫秒",sb.toString(),(e-s));
                return object;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                throw new RuntimeException("发生错误");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在类上使用这个注解
    在这里插入图片描述
    启动服务器,分表请求test1和test2两个方法,可以观察到使用@within注解,可以直接作用在类上。
    在这里插入图片描述
    注意: @within所跟随的注解需要必须是针对CLASS的(@Retention(RetentionPolicy.CLASS)),并且@Target必须是ElementType.TYPE。

    2.3 匹配对象

    2.3.1 @target
    # 拦截mybatis-plus的IService接口的所有的方法
    @Around(value = "target(com.baomidou.mybatisplus.extension.service.IService)")
    
    # 也可以具体拦截某一个类
    @Around(value = "target(com.it2.springbootmybatisplus.service.BookServiceImpl)")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    2.3.2 @bean

    设置前面在指定的bean上

    @Around("bean(bookMapper)")
    
    • 1

    在这里插入图片描述
    示例

    package com.it2.springbootmybatisplus.annotation;
    
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    @Slf4j
    public class MyBeanAspect {
        @Around("bean(bookMapper)")
        public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
            try {
                Signature signature = proceedingJoinPoint.getSignature();
                String className = proceedingJoinPoint.getTarget().getClass().getName();
                String methodName = signature.getName();
                long s=System.currentTimeMillis();
                Object object = proceedingJoinPoint.proceed();
                long e=System.currentTimeMillis();
                StringBuffer sb=new StringBuffer();
                sb.append(className).append(".");
                sb.append(methodName).append("()");
                log.info("方法名:{},执行时间{}毫秒",sb.toString(),(e-s));
                return object;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                throw new RuntimeException("发生错误");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    启动服务器,发起请求,调用bookMapper,可以看到bookMapper被注入了
    在这里插入图片描述
    注意: @Pointcut(“bean(beanName)”),应该指bean的名称而不是类名

    2.3.3 @this

    略,和@target用法很像。

    2.4 匹配参数

    2.4.1 @args

    args参数我们更换一种写法,直接使用AspectJProxyFactory代理工厂
    (1)先声明一个被拦截的目标体,分别声明了不同的参数情形

    package com.it2.springbootmybatisplus.util;
    
    public class TestService {
    
        public void sayHello(String string) {
            System.out.println("sayHello ," + string);
        }
    
        public void sayHello2(Integer a) {
            System.out.println("sayHello2 ," + a);
        }
    
        public void sayHello3(Object object) {
            System.out.println("sayHello3 ," + object);
        }
    
        public void sayHello4(Integer a, Integer b) {
            System.out.println("sayHello4 ," + a + " + " + b + " = " + (a + b));
        }
    
        public void sayHello5() {
            System.out.println("sayHello5");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    (2) 定义切面拦截

    package com.it2.springbootmybatisplus.util;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    import java.util.Arrays;
    import java.util.stream.Collectors;
    
    @Aspect
    public class MyArgAspect {
    
        //@1:匹配只有1个参数其类型是String类型的
        @Pointcut("args(String)")
        public void mypointcut() {
        }
    
    
        @Before("mypointcut()") //引用切点
        public void beforeAdvice(JoinPoint joinpoint) {
            System.out.println("String请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(com.it2.springbootmybatisplus.pojo.User)") //直接声明切点
        public void beforeAdvice2(JoinPoint joinpoint) {
            System.out.println("User请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(com.it2.springbootmybatisplus.pojo.User2)") //直接声明切点
        public void beforeAdvice3(JoinPoint joinpoint) {
            System.out.println("User2请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(Object)") //直接声明切点,直接表明拦截Object
        public void beforeAdvice4(JoinPoint joinpoint) {
            System.out.println("Object请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(Integer,Integer)") //直接声明切点
        public void beforeAdvice5(JoinPoint joinpoint) {
            System.out.println("多参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(*)") //直接声明切点
        public void beforeAdvice6(JoinPoint joinpoint) {
            System.out.println("单个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(*,*)") //直接声明切点
        public void beforeAdvice7(JoinPoint joinpoint) {
            System.out.println("两个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(..)") //直接声明切点
        public void beforeAdvice8(JoinPoint joinpoint) {
            System.out.println("所有的(0个或者多个)请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
        @Before("args(Integer,..)") //直接声明切点
        public void beforeAdvice9(JoinPoint joinpoint) {
            System.out.println("第一个参数是Integer,后面可以有0或者多个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    自定义的Aspect切片,分别拦截String,Integer,User,User2,Object几种参数类型等
    (3) 测试用例

    @Test
        public void testArg(){
            TestService testService=new TestService();
            AspectJProxyFactory proxyFactory=new AspectJProxyFactory();
            proxyFactory.setTarget(testService);
            proxyFactory.addAspect(MyArgAspect.class);
            TestService proxy=proxyFactory.getProxy();
            System.out.println("-------------String-----------------");
            proxy.sayHello("xiaowang");
            System.out.println("-------------Integer-----------------");
            proxy.sayHello2(123);
            System.out.println("--------------User----------------");
            User user=  new User();
            user.setName("User");
            user.setEmail("abc@qq.com");
            proxy.sayHello3(user);
            System.out.println("--------------User2----------------");
            User2 user2=  new User2();
            user2.setName("User2");
            user2.setEmail("abc@qq.com");
            proxy.sayHello3(user2);
            System.out.println("--------------多参数----------------");
            proxy.sayHello4(3,2);
    
            System.out.println("--------------无参数----------------");
            proxy.sayHello5();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    运行测试用例,发现Object拦截了所有(TestService单个参数方法),不同类型的参数需要不同的拦截
    在这里插入图片描述

    2.4.2 @args的其它匹配
    • 单个参数 args(*),等价于args(Object)
        @Before("args(*)") //直接声明切点
        public void beforeAdvice6(JoinPoint joinpoint) {
            System.out.println("单个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 多个参数 args(*,Long),等价于args(Object,Long)
        @Before("args(*,Long)") //直接声明切点
        public void beforeAdvice7(JoinPoint joinpoint) {
            System.out.println("两个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 不限制参数,0个或者多个都可以
        @Before("args(..)") //直接声明切点
        public void beforeAdvice8(JoinPoint joinpoint) {
            System.out.println("所有的(0个或者多个)请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 组合
        @Before("args(Integer,..)") //直接声明切点
        public void beforeAdvice9(JoinPoint joinpoint) {
            System.out.println("第一个参数是Integer,后面可以有0或者多个参数请求参数:" + Arrays.stream(joinpoint.getArgs()).collect(Collectors.toList()));
        }
    
    • 1
    • 2
    • 3
    • 4

    3. 通配符Wildcards

    * 匹配任意数量的字符
    + 匹配指定类及其子类
    .. 一般用于匹配任意数的子包或参数
    
    • 1
    • 2
    • 3

    前面的demo里,我们已经使用到了通配符

    4. 运算符Operators

    &&||!
    • 1
    • 2
    • 3

    可以将多个条件通过运算符进行组合,构成组合式pointcut

    5. pointcut切入点的组合

    利用运算符,我们可以构建组合的pointcut

    @Pointcut("bean(beanName1) || bean(beanName2)")  #beanName1和beanName2其中一个匹配到
    @Pointcut("bean(beanName) && @annotation(xxx.ExecutionTime)")  # beanName和@xxx.ExecutionTime同时匹配到
    @Pointcut("bean(beanName) && !@annotation(xxx.ExecutionTime)") #beanName被匹配到,但是不能包含@xxx.ExecutionTime注解
    
    • 1
    • 2
    • 3

    6. 写法问题

    • 两段式(分离切入点和Advice)
      在这里插入图片描述

    • 直接声明在拦截方法声明切入点
      在这里插入图片描述

    两种声明方式都可以,没有区别。单独声明切入点,可以重复利用,但仅仅式写法上的差异,对实际执行没有任何影响。

    传送门

    利用AOP实现的MyCacheable,(类似@Cacheable)
    springboot基础(67):利用切面编程实现自定义的@MyCacheable

  • 相关阅读:
    团队人才流失怎么办
    logging日志改造---自定义参数传递到格式中
    单页应用(SPA)和多页应用(MPA)的区别和优缺点?
    最新AI创作系统+ChatGPT网站源码+支持GPT4.0+支持ai绘画+支持国内全AI模型
    Ubuntu 18.04上无法播放MP4格式视频解决办法
    控制I/O设备四种方式
    多线程&并发篇---第四篇
    字符集编码(三):Unicode
    【python】with...as语句——基于上下文管理器的操作
    node.js-连接准备
  • 原文地址:https://blog.csdn.net/u011628753/article/details/126797290