AOP,面向切面编程是一种思想,动态代理就是这种思想的最主要表现形式。
实现步骤
1、自定义一个注解类
@Documented //让自定义注解出现在javadoc中 @Target(ElementType.METHOD) //目标对象是方法 @Retention(RetentionPolicy.RUNTIME) //在runtime时生效 public @interface Log { }
2、在serviceimpl中的业务方法上添加这个自定义注解(需要就添加)
3、定义一个切面类,完成通知的逻辑
package com.fujunhao.aop; import com.alibaba.fastjson.JSONObject; import com.fujunhao.Utils.JWTUtils; import com.fujunhao.mapper.OperateLogMapper; import com.fujunhao.pojo.OperateLog; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.Arrays; @Slf4j @Component @Aspect public class LogAspect { /** * 记录操作日志: * 操作人、使用HttpServletRequest解析token,获取payload中的id信息 * 操作时间、当前时间 begin * 执行方法的全类名、 * 执行方法名、方法运行时参数、返回值、 * 方法执行时长 end - begin */ @Autowired private OperateLogMapper operateLogMapper; @Autowired private HttpServletRequest request; @Around("@annotation(com.fujunhao.aop.Log)") public Object createLog(ProceedingJoinPoint joinPoint) throws Throwable { //获取jwt令牌,解析 String token = request.getHeader("token"); //String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpZF91c3JfbmFtZSIsImlkIjoxOCwidXNlcm5hbWUiOiJsaW5waW5nemhpIiwibmFtZSI6Iuael-W5s-S5iyIsImV4cCI6MTczMjc2MDM4Nn0.7k8EcKcaSuIVYYdMO-iEjOfUgfQl0Nx9aANu4NLKAl0"; //调用自定义工具类解析token,返回claims Claims claims = JWTUtils.checkJWT(token); //由于是k-v形式,直接传入key,get到value Integer id = (Integer) claims.get("id"); //获取操作时间 LocalDateTime now = LocalDateTime.now(); long begin = System.currentTimeMillis(); //获取方法的全类名 //首先得到运行时目标对象实例,然后得到class对象,然后返回全类名 String className = joinPoint.getTarget().getClass().getName(); //获取执行方法名字 String methodName = joinPoint.getSignature().getName(); //获取参数 Object[] args = joinPoint.getArgs(); String methodParams = Arrays.toString(args); //获取返回值 Object result = joinPoint.proceed(args); //运行时间 long end = System.currentTimeMillis(); long costTime = end - begin; //因为表中存储的字符串,所以我们直接转json对象为字符串存储 String returnValue = JSONObject.toJSONString(result); OperateLog operateLog = new OperateLog(null, id, now, className, methodName, methodParams, returnValue, costTime); //写日志 operateLogMapper.insert(operateLog); log.info("AOP记录操作日志: {}" , operateLog); return result; } }
通知类型和返回值
5种通知类型
@Around("@annotation(com.fujunhao.aop.Log)")
环绕通知,返回值时object,参数只能使用 ProceedingJoinPoint joinPoint
其他通知,返回值void,参数 JoinPoint joinPoint