一、介绍
在最近的项目中,需要实现对一些重要功能操作记录日志的需求。我们可以在需要的方法中增加记录日志的代码,把记录的日志存到数据库中。Spring AOP 的主要功能就是将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来。在项目中,使用的是AOP的后置通知@AfterReturning,每当后台执行完用户的请求后,执行切面中的内容。
二、实现
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
创建自定义注解@Log
package tech.niua.core.annotation;
import tech.niua.core.enums.BusinessType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
*/
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log
{
public String value() default "";
/**
* 功能
*/
public BusinessType businessType() default BusinessType.OTHER;
}
BusinessType是自己定义的一个业务操作类型的枚举类。
package tech.niua.core.enums;
/**
* 业务操作类型
*
*/
public enum BusinessType
{
/**
* 其它
*/
OTHER,
/**
* 新增
*/
INSERT,
/**
* 修改
*/
UPDATE,
/**
* 删除
*/
DELETE,
/**
* 授权
*/
GRANT,
/**
* 导出
*/
EXPORT,
/**
* 导入
*/
IMPORT,
/**
* 强退
*/
FORCE,
/**
* 生成代码
*/
GENCODE,
/**
* 清空数据
*/
CLEAN,
/**
* 浏览
*/
LIST,
/**
* 添加或修改
*/
INSERTORUPDATE,
/**
* 根据id查询*/
FINDBYID
}
SysOperLog
@Data
@TableName("sys_oper_log")
public class SysOperLog {
@TableId(type = IdType.AUTO)
@Excel(name = "序号", cellType = Excel.ColumnType.NUMERIC)
private Long id;
@Excel(name = "操作")
private String operation;
@Excel(name = "业务类型")
private String businessType;
@Excel(name = "方法名称")
private String method;
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@TableField(exist=false)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTimeBegin;
@TableField(exist=false)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTimeEnd;
@Excel(name = "操作用户")
private String operName;
@Excel(name = "参数")
private String params;
@Excel(name = "请求的ip地址")
private String ip;
}
日志切面类
这里的重点是定义切点以及配置通知。
package tech.niua.admin.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import tech.niua.admin.operlog.domain.SysOperLog;
import tech.niua.admin.operlog.service.ISysOperLogService;
import tech.niua.auth.utils.JwtUtils;
import tech.niua.common.utils.IpUtil;
import tech.niua.core.annotation.Log;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class SysLogAspect {
@Autowired
private ISysOperLogService sysOperLogService;
@Resource
private JwtUtils jwtUtils;
@Value("${jwt.tokenHead}")
private String authTokenStart;
@Autowired
private HttpServletRequest request;
@Value("${jwt.header}")
private String token_header;
//定义切点 @Pointcut
//在注解的位置切入代码
@Pointcut("@annotation(tech.niua.core.annotation.Log)")
public void logPoinCut() {
}
//切面 配置通知
@AfterReturning("logPoinCut()")
public void saveSysLog(JoinPoint joinPoint) {
System.out.println("切面。。。。。");
//保存日志
SysOperLog sysOperLog = new SysOperLog();
//从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//获取切入点所在的方法
Method method = signature.getMethod();
//获取操作
Log log = method.getAnnotation(Log.class);
if (log != null) {
String value = log.value();
sysOperLog.setOperation(value);//保存获取的操作
}
//获取请求的类名
String className = joinPoint.getTarget().getClass().getName();
//获取请求的方法名
String methodName = method.getName();
sysOperLog.setMethod(className + "." + methodName);
//请求的参数
Object[] args = joinPoint.getArgs();
ObjectMapper objectMapper = new ObjectMapper();
String params = "";
try{
//将参数所在的数组转换成json
params = objectMapper.writeValueAsString(args);
}catch (Exception e){
e.printStackTrace();;
}
sysOperLog.setParams(params);
String auth_token = request.getHeader(this.token_header);
if (StringUtils.isNotEmpty(auth_token) && auth_token.startsWith(authTokenStart)) {
auth_token = auth_token.substring(authTokenStart.length());
String username = jwtUtils.getUsernameFromToken(auth_token);
sysOperLog.setOperName(username);
} else {
sysOperLog.setOperName("未授权操作");
}
sysOperLog.setIp(IpUtil.getIpAddr(request));
sysOperLog.setBusinessType(log.businessType().toString());
sysOperLogService.save(sysOperLog);
}
}
在Controller层方法添加@Log注解
三、运行描述
每次执行带有@Log注解的操作后,都会在数据库中添加具体的日志。