记SpringBoot切面编程实现用户操作日志记录保存(自定义注解)

本文介绍了AOP(面向切面编程)的概念、使用场景、优势以及如何通过Spring框架实现自定义注解。重点讲解了切面、连接点、切入点、通知类型和如何在实际操作中应用AOP,如日志记录和API操作跟踪。
摘要由CSDN通过智能技术生成

1、AOP介绍

AOP(Aspect Orient Programming) : 面向切面编程,是一种编程技术,底层通过动态代理实现

1.1、使用场景

当你的某些方法需要增加一些相同的功能时,给业务方法增加非业务功能。可以使用AOP实现。
比如:日志、事务管理、安全等

1.2、作用

减少代码冗余,复用性强;
提高开发效率,使开发人员专注业务逻辑;
代码易维护;

1.3、相关术语

Aspect : 切面,给业务方法增加的功能。

JoinPoint : 连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能。

Pointcut : 切入点,是一个或多个连接点的集合。表示这些方法执行时,都能增加切面的功能。表示切面执行 的位置。

Advice : 通知,表示切面的执行时间,在目标方法之前执行切面还是目标方法之后执行切面。
1、@Before(前置通知):在方法执行前执行
2、@After(后置/最终通知):在方法执行之后执行
3、@Around(环绕通知):在方法执行之前和之后调用的通知
4、@AfterThrowing(异常通知):在方法抛出异常之后。
5、@AfterRunning (后置返回通知):在方法正常返回结果之后执行

target : 目标对象,给哪个对象增加切面的功能,这个对象就是目标对象。

2、自定义注解实现

1、注解定义
创建自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface SysLog {
	//操作描述
    String action() default "";
	//操作类型
    String operateType();
	//记录响应
    boolean printResponse() default true;
	//记录错误
    boolean printError() default true;
}

2、AOP实现

@Aspect
@Configuration
public class SysLogAspect {

    @Autowired
    private AsyncOperation asyncOperation;

    @Autowired
    private Environment env;

    private static final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);

    /**
     * @return void
     */
    @Pointcut("@annotation(com.test.common.annotation.SysLog)")
    public void cutMethod() {
    }

    /**
     * @param proceedingJoinPoint
     * @return java.lang.Object
     * @throws
     */
    @Around("cutMethod()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        SysLogEntity sysLogEntity = new SysLogEntity();
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        SysLog dbLog = method.getAnnotation(SysLog.class);
        try {
            sysLogEntity.setClassName(proceedingJoinPoint.getTarget().getClass().getName());
            //记录归属哪个服务模块
            sysLogEntity.setModuleName(env.getProperty("spring.application.name"));
            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            sysLogEntity.setMethodName(methodSignature.getMethod().getName());
            Object[] args = proceedingJoinPoint.getArgs();
            int bodyIndex = -1;
            List<Integer> paramIndexList = new ArrayList<>();
            List<String> paramNameList = new ArrayList<>();
            int tmpIndex = 0;
            for (Annotation[] parameterAnnotation : method.getParameterAnnotations()) {
                for (Annotation annotation : parameterAnnotation) {
                    if (annotation instanceof RequestBody) {
                        bodyIndex = tmpIndex;
                    } else if (annotation instanceof RequestParam) {
                        RequestParam requestParam = (RequestParam) annotation;
                        String paramName = StringUtils.isEmpty(requestParam.name()) ? requestParam.value() : requestParam.name();
                        paramIndexList.add(tmpIndex);
                        paramNameList.add(StringUtils.isEmpty(paramName) ? "param" + tmpIndex : paramName);
                    } else if (annotation instanceof PathVariable) {
                        PathVariable requestParam = (PathVariable) annotation;
                        String paramName = StringUtils.isEmpty(requestParam.name()) ? requestParam.value() : requestParam.name();
                        paramIndexList.add(tmpIndex);
                        paramNameList.add(StringUtils.isEmpty(paramName) ? "param" + tmpIndex : paramName);
                    }
                }
                tmpIndex++;
            }
            StringBuffer requestBody = new StringBuffer();
            //记录请求参数
 			//body参数
            if (bodyIndex > -1) {
                requestBody.append("RequestBody:").append(JSON.toJSONString(args[bodyIndex]));
                if (!paramIndexList.isEmpty()) {
                    requestBody.append("\n");
                }
            }
            //表单参数
            if (!paramIndexList.isEmpty()) {
                requestBody.append("RequestParam:");
                for (int i = 0; i < paramIndexList.size(); i++) {
                    if (i > 0) {
                        requestBody.append("&");
                    }
                    requestBody.append(paramNameList.get(i)).append("=").append(args[paramIndexList.get(i)]);
                }
            }
            sysLogEntity.setRequestData(requestBody.toString());
            String actionDesc = dbLog.action();
            if (StringUtils.isEmpty(actionDesc)) {
                ApiOperation annotation = methodSignature.getMethod().getAnnotation(ApiOperation.class);
                if (annotation != null) {
                    actionDesc = annotation.value();
                }
            }
            sysLogEntity.setUserId(AuthUtil.getUser().getId() + "");
            sysLogEntity.setRequestIp(WebUtil.getIP());
            sysLogEntity.setOperateType(dbLog.operateType());
            sysLogEntity.setActionDesc(actionDesc);
            Object result = proceedingJoinPoint.proceed();
            if (dbLog.printResponse()) {
                sysLogEntity.setResponseData(result.toString());
            }
            sysLogEntity.setStatus(HttpStatus.HTTP_OK);
            return result;
        } catch (Exception ex) {
            if (dbLog.printError()) {
                sysLogEntity.setErrMsg(ExceptionUtils.getErrMsg(ex));
            }
            sysLogEntity.setStatus(HttpStatus.HTTP_INTERNAL_ERROR);
            throw ex;
        } finally {
            sysLogEntity.setCreateTime(new Date());
            //调用外部接口,将记录发送过去记录(异步发送)
            asyncOperation.addLog(sysLogEntity);
        }
    }

}

3、应用示例

	@ApiOperation("查询系统用户详情(不脱敏)")
  	@GetMapping(value = "/getDetailUserInfo")
    @SysLog(operateType = "查看敏感信息")
    public Result<SimpleUserInfo> getDetailUserInfo(@RequestParam String userId) {
        return Result.succeed(sysUserInfoService.getDetailUserInfo(userId));
    }

最终可以看到操作记录已经保存到数据库
在这里插入图片描述

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

折戏花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值