需求
● 每当有增、删、改、上传等编辑操作时,需要记录日志到数据库,用于溯源。
● 记录字段比如:操作人id、名称、ip地址、请求方式、调用方法、参数、操作类型、操作时间、操作模块、返回结果。
使用AOP
切面编程方式,对增删改操作进行增强,在操作完成后切入,获取所需要的信息,存入数据库。
创建自定义注解
● 使用@interface
创建自定义注解OptLog
。
● 1个参数:optType
,用于记录操作类型。
1 2 3 4 5 6 7 8 9 10 11
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface OptLog {
String optType() default "";
}
|
创建常量,定义操作类型
定义常量做为optType
的值,统一规范,方便后期维护。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class OptTypeConst {
public static final String SAVE_OR_UPDATE = "新增或修改";
public static final String SAVE = "新增";
public static final String UPDATE = "修改";
public static final String REMOVE = "删除";
public static final String UPLOAD = "上传";
}
|
编写切面方法,实现记录日志
● @Aspect
:声明为切面类。
● @Pointcut("@annotation(com.minzheng.blog.annotation.OptLog)")
:在OptLog注解的位置切入代码。
● @AfterReturning(value = "optLogPointCut()", returning = "keys")
:表示对optLogPointCut()
方法定义的切入点进行增强的实现方法,returning = “keys”表示返回的参数。
● 通过RequestContextHolder
获取到request
,获取请求中的信息。
● joinPoint
通过反射,获取到切入点所在的方法,以及注解中的内容。
● 将信息存入operationLog
实体类,最终通过operationLogDao
存入数据库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| @Aspect @Component public class OptLogAspect {
@Autowired private OperationLogDao operationLogDao;
@Pointcut("@annotation(com.minzheng.blog.annotation.OptLog)") public void optLogPointCut() {}
@AfterReturning(value = "optLogPointCut()", returning = "keys") @SuppressWarnings("unchecked") public void saveOptLog(JoinPoint joinPoint, Object keys) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = (HttpServletRequest) Objects.requireNonNull(requestAttributes).resolveReference(RequestAttributes.REFERENCE_REQUEST); OperationLog operationLog = new OperationLog(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Api api = (Api) signature.getDeclaringType().getAnnotation(Api.class); ApiOperation apiOperation = method.getAnnotation(ApiOperation.class); OptLog optLog = method.getAnnotation(OptLog.class); operationLog.setOptModule(api.tags()[0]); operationLog.setOptType(optLog.optType()); operationLog.setOptDesc(apiOperation.value()); String className = joinPoint.getTarget().getClass().getName(); String methodName = method.getName(); methodName = className + "." + methodName; operationLog.setRequestMethod(Objects.requireNonNull(request).getMethod()); operationLog.setOptMethod(methodName); operationLog.setRequestParam(JSON.toJSONString(joinPoint.getArgs())); operationLog.setResponseData(JSON.toJSONString(keys)); operationLog.setUserId(UserUtils.getLoginUser().getId()); operationLog.setNickname(UserUtils.getLoginUser().getNickname()); String ipAddress = IpUtils.getIpAddress(request); operationLog.setIpAddress(ipAddress); operationLog.setIpSource(IpUtils.getIpSource(ipAddress)); operationLog.setOptUrl(request.getRequestURI()); operationLogDao.insert(operationLog); }
}
|
段落引用知识点
@Around @Before @After @AfterReturning 执行顺序为:
● Around
● AroundBefore
● before
● method.invoke()
● AroundAfter
● After
● AfterReturning