SpringBoot AOP

发布时间:2024年01月10日

依赖引入

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

代码实现

以给公共字段注入值为例

公共字段与枚举类:

private LocalDateTime createTime;

private LocalDateTime updateTime;

private Long createUser;

private Long updateUser;
/**
 * 数据库操作类型
 */
public enum OperationType {

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

自定义注解

自定义注解 @AutoFill

/**
 * 自定义注解,用于标识某个方法需要进行功能字段自动填充
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //数据库操作类型:UPDATE、INSERT
    OperationType values();
}

AOP实现类

/**
 * 自定义切片,实现公共字段字段填充
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 切入点
     * @Pointcut注解表示切入点的表达式有多种,最常用的是两种,execution表达式和@annotation注解;
     * execution(* com.sky.mapper.*.*(..)) 表示 com.sky.mapper包下所有类的所有方法
     * @annotation(com.sky.annotation.AutoFill) 表示 com.sky.annotion包下的AutoFill注解
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut(){}

    /**
     * 前置通知,在通知中进行公共字段赋值
     * 可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;
     * @Before("autoFillPointCut()") 表示接入点为autoFillPointCut()的切入点
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint){
        log.info("开始公共字段自动填充...");
        //获取到当前被拦截的方法上的数据库操作类型
        MethodSignature signature=(MethodSignature) joinPoint.getSignature();//对象方法签名
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType values = autoFill.values();//获得数据库操作对象
        //获取到当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if(args==null||args.length==0) {return;}
        Object entity = args[0];
        //准备赋值的数据
        LocalDateTime localDateTime = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();
        //根据当前不同的操作类型,为对应属性通过反射来赋值
        if(values==OperationType.INSERT){
            try {
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                setCreateTime.invoke(entity,localDateTime);
                setCreateUser.invoke(entity,currentId);
                setUpdateTime.invoke(entity,localDateTime);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else if(values==OperationType.UPDATE){
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                setUpdateTime.invoke(entity,localDateTime);
                setUpdateUser.invoke(entity,currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

核心注解和类

  1. @Aspect ,表示当前类是一个切面类,用于类上
  2. @Before ,前置通知,用于方法上,被@Before注解标注的方法会在被切入方法执行之前执行
  3. @After ,后置通知,用于方法上,被@After 注解标注的方法会在被切入方法执行之后执行
  4. @AfterRetruning ,返回通知,用于方法上,被@AfterRetruning 注解标注的方法会在被切入方法返回结果之后执行
  5. @AfterThrowing,异常通知,用于方法上,被@AfterThrowing 注解标注的方法会在被切入方法抛出异常之后执行,一般用于有目的地获取异常信息
  6. @Aroud ,环绕通知,用于方法上,被@Aroud 注解标注的方法会在被切入方法执行前后执行
  7. @Pointcut,切入点,标注在方法上,用于定义切入点,切入点就是指哪些连接点初进行切入。@Pointcut注解表示切入点的表达式有多种,最常用的是两种,execution表达式和注解;
    1. execution:用于匹配方法执行的连接点(路径)
    2. @annotation:用于匹配当前执行方法持有指定注解的方法
  8. Jointpoint,连接点,连接点是指被aop切面切入的位置。可以joinpoint中得到命中方法的相关信息,利用这些信息可以做一些额外的业务操作;

标记切入点的常用方法

execution表达式

表达式法:访问修饰符 返回值 包名.包名…类名.方法(参数列表)

//1. 表示匹配所有com.fanfu包以及子包下的所有类中以add开头的方法,返回值、参数不限;
@Pointcut("execution(* com.fanfu..*.*.add*(..))")
//2. 表示匹配所有com.fanfu包以及子包下的所有类中以add开头,参数类型是String的方法,返回值不限;
@Pointcut("execution(* com.fanfu..*.*.add*(String))")
//3. 表示匹配com.fanfu包下任意类、任意方法、任意参数;
@Pointcut("execution(* com.fanfu..*.*.*(String))")
  • execution()为表达式的主体;
  • 第一个*表示返回值类型为任意,即不限制返回值类型;
  • 包后的*表示当前包,包后面连续两个…表示当前包以及子包;(…)表示任意参数;
  • 最后的*.*(…)表示匹配任意类、任意方法、任意参数;

注解

注解语法:@annotation(自定义的注解)

//表示有@AutoFill注解的方法
@Pointcut("@annotation(com.sky.annotation.AutoFill)")
文章来源:https://blog.csdn.net/weixin_57025392/article/details/135512017
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。