浅谈-SpringAop增强方法功能

当我们进行全链路trace、监控接口性能、日志记录时,可能需要大量的代码堆积到已有的逻辑中,为了避免这种冗余的代码,我们可以使用spring的aop功能,动态织入目标方法来增强逻辑。
它的基本概念我这里就不陈述了。
我这里用到了两种方式来进行实现aop,1、注解形式(功能实现后可通过注解直接作用在目标方法上),2、通过类全限定名-方法名匹配 两种方式可以交叉使用

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PerfAnnotation {
    String name() default "";
}
@Component
@Aspect
@Slf4j
@Order(1)
public class PerfAnnotationAop {
    
    
    // *************************************************
    /* 注解形式 */
    // *************************************************
    // 定义一个切入点
    @Pointcut(value = "@annotation(com.example.demo_fengyifan.annotation.PerfAnnotation)")
    public void perfAnnotationMethod() {
    // 仅作为一个切入点,perfAnnotationMethod方法里面的内容不会执行
    }

    // 环绕增强 可以在目标方法执行前后 织入一些逻辑
    @Around("perfAnnotationMethod()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("目标方法执行前======");
            Object proceed = pjp.proceed(); // 目标方法执行
            System.out.println("目标方法执行后======");
            // 通过反射获取该类的所有方法
            for (Method method : pjp.getTarget().getClass().getMethods()) {
                System.out.println("method.getName() = " + method.getName());
            }

            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            String name = method.getName(); // 目标方法名
            System.out.println("name = " + name); // 这种方式获取的方法名和pjp.getSignature().getName()相同

            System.out.println("pjp.getSignature().getName() = " + pjp.getSignature().getName()); // 具体目标方法名
            System.out.println("pjp.getSignature().getDeclaringTypeName() = " + pjp.getSignature().getDeclaringTypeName());// 类名全路径(类的全限定名)
            System.out.println("proceed = " + proceed); // 目标方法执行后的返回值,如果方法是void修饰,proceed就是null
            return proceed;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            String msg = pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName();
            System.out.println("msg = " + msg);
        }
    }


    // *************************************************
    /* 类的全限定名形式 */
    // *************************************************
    @Pointcut("execution(* com.example.demo_fengyifan.controller.UserController.show2(..))")
    public void checkpoint() {
    }

    // 目标方法执行前 在执行该方法
    @Before("checkpoint()")
    public void before(JoinPoint point) {
        // args是请求参数
        Object[] args = point.getArgs();
        if (args.length == 0) return;
        BBB bbb = (BBB) args[0]; //执行前
        System.out.println("执行前 bbb = " + bbb);
    }

    // 目标方法执行后 在执行该方法
    @AfterReturning("checkpoint()")
    public void after(JoinPoint point) {
        // args是请求参数,这个请求参数是被加工过的,如果目标方法对请求参数重新赋值,这里获取到的是赋值后的参数
        Object[] args = point.getArgs();
        if (args.length == 0) return;
        BBB bbb = (BBB) args[0];
        System.out.println("bbb = " + bbb);
    }
}

controller层

@RestController
@Api(tags = "用户的Controller", hidden = true)
public class UserController {

    @Resource
    UserService userService;

    @PostMapping("/test111")
    public void showwww(@RequestBody AAA aaa) {

        List<BBB> tests = new LinkedList<>();
        List<CCC> cccs = aaa.getCccs();
        tests = cccs.stream().map(CCC::toBBB).collect(Collectors.toList());
        System.out.println("collect = " + tests);

        System.out.println("aaa = " + aaa);
    }

    @PostMapping("/test_bbb")
    public void show2(@RequestBody BBB bbb) {
        System.out.println("bbb = " + JSONUtils.write(bbb));
        System.out.println(bbb.getB());
        BBB bbb1 = new BBB();
        bbb1.setId(111);
        System.out.println("bbb1 = " + bbb1);
        bbb.setId(88888);
    }

    @GetMapping("/test/runAsync")
    @PerfAnnotation
    public String showRunAsync() {
        System.out.println(";;;;;");
        CompletableFuture.runAsync(() -> {
            try {
                userService.show1();
                userService.show2();
                userService.show3();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        System.out.println("异步处理了!!!!!");
        return "ok";
    }
}
@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@JsonIgnoreProperties(ignoreUnknown = true)
@NoArgsConstructor
@AllArgsConstructor
public class BBB {
    private Integer id;
    private Boolean b = false;
}

补充下
如何获取自定义注解内的属性值?

@PerfAnnotation(name = “showRunAsync”) 自定义注解内 name = “showRunAsync”

    @GetMapping("/test/runAsync")
    @PerfAnnotation(name = "showRunAsync")
    public String showRunAsync() {
        return "ok";
    }

在进行aop增强功能时 这样来做

@Around("perfAnnotationMethod()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {
            // 获取目标方法的类对象
            Class<?> aClass = pjp.getTarget().getClass();

            // pjp.getSignature().getName() 具体目标方法名
            System.out.println("pjp.getSignature().getName() = " + pjp.getSignature().getName());

            // 根据类对象 获取到目标方法的信息
            Method declaredMethod = aClass.getDeclaredMethod(pjp.getSignature().getName());

            // 根据目标方法 找到自定义注解对象
            PerfAnnotation annotation = declaredMethod.getAnnotation(PerfAnnotation.class);

            // 打印注解内的信息
            System.out.println("annotation name = " + annotation.name());
            
        }
    }

在这里插入图片描述

在这里插入图片描述

获取自定义注解内的参数
注意的地方:如果两个方法都被@PerfAnnotation注解修饰了,并且这两个方法在同一个类中,然后在方法一内调用方法二时,方法二的注解会失效,解决方法:把方法二放到别的类中 或者指明当前类然后调用方法二 看下图
在这里插入图片描述

    @PostMapping("/test_bbb")
    @PerfAnnotation(name = "fff")
    public void showyifan(@RequestBody BBB bbb) {
        System.out.println("bbb = " + JSONUtils.write(bbb));
        System.out.println(bbb.getB());
        BBB bbb1 = new BBB();
        bbb1.setId(111);
        System.out.println("bbb1 = " + bbb1);
        bbb.setId(88888);
        userController.showyifanhaha(99999);
    }

    @PerfAnnotation(name = "DDD")
    public void showyifanhaha(Integer id) {
        System.out.println("id = " + id);
    }
@Around("perfAnnotationMethod()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {

            Thread.sleep(2000);
            // 通过反射获取该类的所有方法
            for (Method method : pjp.getTarget().getClass().getMethods()) {
                System.out.println("method.getName() = " + method.getName());
            }

            // 获取目标方法的类对象
            Class<?> aClass = pjp.getTarget().getClass();

            // pjp.getSignature().getName() 具体目标方法名
            System.out.println("pjp.getSignature().getName() = " + pjp.getSignature().getName());

            // ==========================================================================================
            // 获取自定义注解内的参数 方式一:
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Class<?>[] parameterTypes = signature.getMethod().getParameterTypes();

            Class[] classes = new Class[pjp.getArgs().length];

            for (int i = 0; i < pjp.getArgs().length; i++) {
                Class<?> parameterType = parameterTypes[i];
                System.out.println("parameterType = " + parameterType);
                classes[i]=parameterType;
            }

            // 根据类对象 获取到目标方法的信息
            Method declaredMethod = aClass.getDeclaredMethod(pjp.getSignature().getName(), classes);

            // 根据目标方法 找到自定义注解对象
            PerfAnnotation annotation = declaredMethod.getAnnotation(PerfAnnotation.class);

            // 打印注解内的信息
            System.out.println("annotation name = " + annotation.name());
            // ==========================================================================================


            System.out.println("目标方法执行前======");
            Object proceed = pjp.proceed(); // 目标方法执行
            System.out.println("目标方法执行后======");


            // ==========================================================================================
            // 获取自定义注解内的参数 方式二:这种方式看起来比上面的要好一些
            MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
            Method method = methodSignature.getMethod();
            PerfAnnotation annotation1 = method.getAnnotation(PerfAnnotation.class);
            System.out.println("annotation1 name = " + annotation1.name());
            String name = method.getName(); // 目标方法名
            System.out.println("name = " + name); // 这种方式获取的方法名和pjp.getSignature().getName()相同
            // ==========================================================================================


            System.out.println("pjp.getSignature().getName() = " + pjp.getSignature().getName()); // 具体目标方法名
            System.out.println("pjp.getSignature().getDeclaringTypeName() = " + pjp.getSignature().getDeclaringTypeName());// 类名全路径(类的全限定名)
            System.out.println("proceed = " + proceed); // 目标方法执行后的返回值,如果方法是void修饰,proceed就是null
            return proceed;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            String msg = pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName();
//            System.out.println("msg = " + msg);
        }
    }```

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值