Spring Boot AOP 切面编程

Spring Boot AOP 切面编程

概述

  • 所有函数都要加代码, 繁琐;

demo

概念

执行流程

  • 使用代理对象 进行注入;

进阶

切点引用 pointCut

  • pt 函数改为 public , 其他aspect 类中也可以引用

通知顺序

切入点表达式

execution

  • 只写方法名, 会匹配所有同名方法; ;因此不建议省略包名类名;

annotation

连接点

案例 记录日志

获取token

  • 获取token直接从请求header中获取,不要从后端其他类中获取
package org.hzl.aop;  


import com.alibaba.fastjson.JSONObject;  
import io.jsonwebtoken.Claims;  
import jakarta.servlet.http.HttpServletRequest;  
import lombok.extern.slf4j.Slf4j;  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.hzl.mapper.OperateLogMapper;  
import org.hzl.pojo.OperateLog;  
import org.hzl.utils.JwtUtils;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.stereotype.Component;  

import java.time.LocalDateTime;  
import java.util.Arrays;  

@Slf4j  
@Component  
@Aspect  
public class LogAspect {  

    @Autowired  
    private OperateLogMapper operateLogMapper;  

    @Autowired  
    private HttpServletRequest request;  

    @Around("@annotation(org.hzl.anno.Log)")  
    public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {  


        // 获取emp 姓名 , 从 request 中jwt 令牌中获取  
        String token = request.getHeader("token");  
        Claims claims = JwtUtils.parseToken(token);  
        Integer operateUser = (Integer) claims.get("id");  

        // 操作时间  

        LocalDateTime operateTime = LocalDateTime.now();  

        //  class name  

        String className = joinPoint.getTarget().getClass().getName();  

        //  方法名  

        String methodName = joinPoint.getSignature().getName();  

        // 参数  

        String methodParams = Arrays.toString(joinPoint.getArgs());  

        // 返回值 将返回值 转陈 json 类型字符串  

        long begin = System.currentTimeMillis();  
        Object result = joinPoint.proceed();  
        long end = System.currentTimeMillis();  


        String resultValue = JSONObject.toJSONString(result);  

        // 方法 耗时  

        long costTime = end - begin;  


        OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,resultValue,costTime);  
        operateLogMapper.insert(operateLog);  

        log.info("AOP中操作日志:{}",operateLog);  


        return result;  
    }  
}
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
public @interface Log {  
}
  • Log 注解
  • aop 根据 @Log 注解 进行包裹.
  • 在controller中的 增 删 改 方法前使用@Log 注解标记
@Log  
@DeleteMapping("/{id}")  
public Result delete(@PathVariable Integer id){  
    deptService.delete(id);  
    log.info("根据日志输出{}",id);  
    return Result.success();  
}  


@Log  
@PostMapping  
public Result add(@RequestBody Dept dept){  
    log.info("新增部门{}",dept);  
    deptService.add(dept);  
    return Result.success();  
}

获取注解中的参数

  • 指定注解可以接受value 参数, 类型是某个枚举类型

    @Target(ElementType.METHOD)  
    @Retention(RetentionPolicy.RUNTIME)  
    // @interface 表示注解  
    public @interface AutoFill {  
        //UPDATE INSERT  
        // 注解中会出现  @AutoFill(value = ) 等于的值为 OperationType中指定的  
        OperationType value();  
    }
    
  • 获取参数 在 @Before("@annotation(autoFill)" ) 中填入注解形参的名字, 函数第二个参数设置log注解即可;

    @Slf4j  
    @Component  
    @Aspect  
    public class AutoFillAspect {  
        /**  
         * 切入点  
         */  
        @Pointcut("@annotation(com.sky.annotation.AutoFill)")  
        public void pt(){};  
    
    
        @Before("@annotation(autoFill)" )  
        public void AutoFill(JoinPoint joinPoint,AutoFill autoFill) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {  
            log.info("开始执行公共字段填充");  
            //拦截mapper方法 update操作 只用 updateTime updateUser 赋值即可  
    
    
            // 获取方法的参数实体对象  
            OperationType operationType = autoFill.value();  
            log.info("获取到传入value:{}",operationType);