Spring AOP与自定义注解实现共性需求

启发
Spring AOP可以帮助我们Java在不修改源代码的前提下实现功能增强,其底层实现基于Java动态代理或者CGLIB
在这里插入图片描述
以往我们使用execution选定具体利用AOP扩展哪些类
execution(* com.itlaoqi…Service.(…))
这样使用非常不灵活,因为并不是Service中所有的方法都需要被增强
其实受到Spring声明式事务注解@Transactional的启发,我们在项目中利用自定义注解实现了大量共性需求。

应用场景

  1. 需要灵活使用共性需求的地方都可以使用该方案
  2. 收集上报指定关键方法的入参、执行时间、返回结果等关键信息,用作后期调优
  3. 关键方法在幂等性前置校验(基于本地消息表)
  4. 类似于Spring-Retry模块,提供关键方法多次调用重试机制
  5. 提供关键方法自定义的快速熔断、服务降级等职责
  6. 关键方法的共性入参校验
  7. 关键方法在执行后的扩展行为,例如记录日志、启动其他任务等

开发过程
新建Spring Boot 2.x工程,在pom.xml中增加aspectjweaver依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

新建自定义注解,利用@interface关键字定义注解

package com.itlaoqi.aopanno.annoation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解用在方法上
@Target(ElementType.METHOD)
//@Retention作用是定义被它所注解的注解保留多久,RUNTIME运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodExporter {

}
开发切面类
package com.itlaoqi.aopanno.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.Date;

//关键代码1:说明当前对象是一个切面
@Aspect
//关键代码2:@Component允许在Spring IOC对当前对象实例化并管理
@Component
@Slf4j
public class MethodExporterAspect {
    //关键代码3:说明切面的作用范围,任何增加@MethodExporter的目标方法都将在执行方法前先执行该切面方法
    //@Around环绕通知,最强大的通知类型,可以控制方法入参、执行、返回结果等各方面细节
    @Around("@annotation(com.itlaoqi.aopanno.annoation.MethodExporter)")
    public Object methodExporter(ProceedingJoinPoint joinPoint) throws Throwable {
        long st = new Date().getTime();
        //执行目标方法,获取方法返回值
        Object proceed = joinPoint.proceed();
        long et = new Date().getTime();


        ObjectMapper mapper = new ObjectMapper();
        //将入参JSON序列化
        String jsonParam = mapper.writeValueAsString(joinPoint.getArgs());
        //将返回结果JSON序列化
        String jsonResult = null;
        if(proceed != null){
            jsonResult = mapper.writeValueAsString(proceed);
        }else{
            jsonResult = "null";
        }
        //模拟上报过程
        log.debug("正在上报服务器调用过程:\ntarget:{}.{}()\nexecution:{}ms,\nparameter:{}\nresult:{}"
                ,joinPoint.getTarget().getClass().getSimpleName()
                ,joinPoint.getSignature().getName()
                ,(et-st)
                ,jsonParam
                ,jsonResult);
        return proceed;
    }
}
目标方法上增加自定义注解
@RestController
public class SampleController {
    @MethodExporter
    @GetMapping("/list")
    public Map list(int page , int rows){
        Map result = new LinkedHashMap();
        result.put("code", "0");
        result.put("message", "success");

        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

DEBUG 1892 — [nio-8080-exec-1] c.i.aopanno.aspect.MethodExporterAspect : 正在上报服务器调用过程:
target:SampleController.insert
execution:602ms,
parameter:[]
result:{“code”:“404”,“message”:“NOT FOUND”}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的老史

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值