操作日志包括:操作人、操作时间、执行方法得全类名、执行方法名、方法运行时的参数、返回值、方法执行时长
主要使用环绕通知@Around,以及@Annotation
思路分析:
如果要保存到数据库里面,首先需要一个数据库表:
-- 操作日志表
create table operate_log(
id int unsigned primary key auto_increment comment 'ID',
operate_user int unsigned comment '操作人ID',
operate_username varchar(100) comment '操作人姓名',
operate_time datetime comment '操作时间',
class_name varchar(100) comment '操作的类名',
method_name varchar(100) comment '操作的方法名',
method_params varchar(1000) comment '方法参数',
return_value varchar(2000) comment '返回值',
cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';
有了一个数据库表以后,我们需要创建对应的Mapper文件(这里使用的是注解insert),放到Mapper层即可
public interface OperateLogMapper {
//插入日志数据
@Insert("insert into operate_log (operate_user,operate_username ,operate_time, class_name, method_name, method_params, return_value, cost_time) " +
"values (#{operateUser},#{operateUsername} ,#{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
public void insert(OperateLog log);
}
紧接着创建一个我们自己的自定义注解Log:里面不需要写功能性代码,Log仅仅起到标明作用:
//单纯起到标识作用
@Retention(RetentionPolicy.RUNTIME)//这个注解什么时候生效?运行的时候
@Target(ElementType.METHOD)//注解作用在什么地方?这里是方法上
public @interface Log {
}
新创建一个实体类:OperateLog
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
private Integer id; //ID
private Integer operateUser; //操作人ID
private String operateUsername; //操作人姓名
private LocalDateTime operateTime; //操作时间
private String className; //操作类名
private String methodName; //操作方法名
private String methodParams; //操作方法参数
private String returnValue; //操作方法返回值
private Long costTime; //操作耗时
}
以上工作都完成以后,创建aop切面:
@Component
@Slf4j
@Aspect
public class LogAspect {
//需求:将增删改的相关接口的操作日志记录保存到数据库表里面
//获取请求头
@Autowired
private HttpServletRequest servletRequest;
@Autowired
private OperateLogMapper operateLogMapper;
@Around("@annotation(com.example.anno.Log)")
public Object LogAround(ProceedingJoinPoint joinPoint) throws Throwable {
//1.执行前
//1.1获取操作人ID
//利用jwt令牌进行获取 -- 首先获取到请求头 --- 然后解析jwt令牌
String jwt = servletRequest.getHeader("token");
Claims claims = JwtUtils.parseJWT(jwt);
Integer operateUser =(Integer) claims.get("id");
String operateUsername =(String) claims.get("username");
//1.2获取操作时间
LocalDateTime LocalDateTime = java.time.LocalDateTime.now();
//1.3获取操作类名
String className = joinPoint.getTarget().getClass().getName();
//1.4获取操作方法名
String methodName = joinPoint.getSignature().getName();
//1.5获取操作方法参数
Object[] args = joinPoint.getArgs();
String methodParams = Arrays.toString(args);
long begin = System.currentTimeMillis();
//2.执行原方法
Object proceed = joinPoint.proceed();
long end = System.currentTimeMillis();
//1.6获取操作方法返回值
String returnValue = JSONObject.toJSONString(proceed);
//1.7获取操作耗时
long costTime = end - begin;
//2.1记录日志 -- 调用对应的Mapper方法
OperateLog operateLog = new OperateLog(null,operateUser,operateUsername,LocalDateTime,className,methodName,methodParams,returnValue,costTime);
operateLogMapper.insert(operateLog);
log.info("记录日志操作:{}",operateLog);
//3.返回值
return proceed;
}
}
这里使用到了工具类JwtUtils:也就是1.1
public class JwtUtils {
private static String signKey = "itxiaoli";
private static Long expire = 43200000L;
/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String generateJwt(Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
1.6部分是将数据转换为json格式,使用到的是alibaba依赖:pom文件添加依赖:
<!-- fastJson 手动转换json格式数据-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<!--AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
结构如下:(只看对应的即可)
效果如下: