Springboot AOP实现操作记录日志

概要

在已实现日志插入功能的基础上,使用AOP优化代码

使用方式
	// 在controller层的方法上填写注解,下面则是我在编辑用户接口上写入的例子,#{#xxx}都会被替换为xxx对应参数值。
	@OperationLogAnnotation(actionModule = "user", actionType = "编辑", actionUrl = "/admins/users/#{#uid}", actionContent = "#{#users}")
    @PutMapping("/users/{uid}")
    public JsonResult update(@RequestBody @Validated UsersUpdateParam users, @PathVariable Integer uid) {...}
	/* 例如 发起请求中,uid为1,users为{phone:12345}的对象
	   日志则会保存为 {actionModule:"user",
					actionType:"编辑",
					actionUrl:"/admins/users/1",
					actionContent:"{phone:12345}"}
	*/
	
导入依赖
<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>fastjsonartifactId>
    <version>1.2.58version>
dependency>
定义日志注解
package com.xyongfeng.aop;
import java.lang.annotation.*;

@Target(ElementType.METHOD)// 注解放置的目标位置即方法级别
@Retention(RetentionPolicy.RUNTIME)// 注解在哪个阶段执行
@Documented
public @interface OperationLogAnnotation {

    String actionModule(); // 操作模块

    String actionType();  // 操作类型

    String actionUrl();  // 操作路径

    String actionContent() default "";  // 操作内容
}
编写切面类
package com.xyongfeng.aop;


import com.alibaba.fastjson.JSONObject;
import com.xyongfeng.pojo.JsonResult;
import com.xyongfeng.service.AdminLogService;
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.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Aspect
@Component
public class OperationLogAspect {

    /**
     * 日志业务类
     */
    @Autowired
    private AdminLogService adminLogService;

    /**
     * 设置操作日志切入点,在注解的位置切入代码
     */
    @Pointcut("@annotation(com.xyongfeng.aop.OperationLogAnnotation)")
    public void operLogPoinCut() {
    }

    @AfterReturning(returning  /**
     * 记录操作日志
     * @param joinPoint 方法的执行点
     * @param result  方法返回值
     * @throws Throwable
     */ = "result", value = "operLogPoinCut()")
    public void saveOperLog(JoinPoint joinPoint, JsonResult result) {
        // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();

        //获取注解信息
        OperationLogAnnotation annotation = method.getAnnotation(OperationLogAnnotation.class);
        if (annotation != null) {
            //获取参数值
            Object[] parameterArgs = joinPoint.getArgs();
            //获取参数值类型
            // Class[] parameterTypes = method.getParameterTypes();
            //获取参数名
            String[] parameterNames = new DefaultParameterNameDiscoverer().getParameterNames(method);


            //建立上下文字典,储存参数信息
            StandardEvaluationContext context = new StandardEvaluationContext();
            for (int i = 0; i < parameterArgs.length; i++) {
                if (parameterNames[i] != null){
                    // JSONObject.toJSONString  可以将对象转换为json字符串
                    context.setVariable(parameterNames[i], JSONObject.toJSONString(parameterArgs[i]));
                }

            }
            
            
            // 使用SpEL表达式替换文本
            SpelExpressionParser parser = new SpelExpressionParser();
            // 替换url
            Expression urlExpression = parser.parseExpression(annotation.actionUrl(), ParserContext.TEMPLATE_EXPRESSION);
            // 替换content
            Expression contentExpression = parser.parseExpression(annotation.actionContent(), ParserContext.TEMPLATE_EXPRESSION);


            // 读取注解信息,执行替换,再进行日志插入
            adminLogService.insert(
                    annotation.actionModule(),
                    annotation.actionType(),
                    (String) urlExpression.getValue(context),
                    (String) contentExpression.getValue(context),
                    result.getCode().equals(200));
           	// result.getCode().equals(200) 则是返回操作结果是否成功

        }

    }

}

参考链接

SpringBoot+AOP实现用户操作日志的记录: https://blog.csdn.net/qq_42570879/article/details/108830089

Java注解动态解析获取方法参数值: https://blog.csdn.net/qq_21480147/article/details/120063385

Spring表达式实现变量替换: https://blog.csdn.net/zhigang0529/article/details/83178167

你可能感兴趣的:(spring,spring,boot,java,后端)