哈喽,我们好 最近领导让写个用切面完成日志保存,通过调研和亲测,以完美解决。在这里分享给我们,给有需求的码友直接使用。直接上代码:

依赖引入:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

创立表:

DROP TABLE IF EXISTS `logger_record`;
CREATE TABLE `logger_record`  (
  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'id',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描绘',
  `package_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作所在的包',
  `class_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作的类名',
  `method_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作的办法名',
  `request_params` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作办法的参数',
  `response_params` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '操作办法的出参',
  `running_time` bigint NULL DEFAULT NULL COMMENT '运转时间',
  `created_by` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创立人ID',
  `date_created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

对象实体类

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.**.app.common.util.TimeUtils;
import lombok.Data;
import org.springframework.data.annotation.Id;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Date;
/**
 * Created by cb on 2022/11/15 10:07
 */
//@Entity
@Data
public class LoggerRecord implements Serializable {
    @Id
    @TableId(value = "id",type = IdType.ID_WORKER_STR)//可不引入注解,UUID生成
    private String id;
    /**
     * 操作办法的描绘
     */
    private String description;//实际没用上
    /**
     * 操作所在的包
     */
    private String packageName;
    /**
     * 操作的类名
     */
    private String className;
    /**
     * 操作的办法名
     */
    private String methodName;
    /**
     * 操作办法的参数
     */
    private String requestParams;
    /**
     * 操作办法的出参
     */
    private String responseParams;
    /**
     * 运转时间
     */
    private Long runningTime;
    /** 创立时间 */
    @JsonFormat(pattern = TimeUtils.PATTERN2)
    private LocalDateTime dateCreated;//能够用date
    /** 创立人ID */
    private String createdBy;//实际没用上
}

注解办法:


package com.**.app.server.annotation;
import java.lang.annotation.*;
/**
 * Created by cb on 2022/11/15 10:05
 * 日志描绘的注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoggerManager {
}

service办法,可根据自己项目去相应改动:

/**
 * 日志增修改的接口
 * Created by cb on 2022/11/15 10:11
 */
@Service
public class LoggerRecordRepository extends ServiceImpl<LoggerRecordMapper, LoggerRecord> {
}

工具类:

package com.**.app.server.util;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
 * Created by cb on 2022/11/15 10:24
 */
public class AopUtil {
    /**
     * 获取切点处的办法
     * @param joinPoint
     * @return
     */
    public static Method getMethod(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return method;
    }
    /**
     //     * 将参数数组转化为字符串
     //     * @param params  参数数组
     //     * @return 参数字符串
     //     */
    public static String getStringOfParams(Object[] params) {
        if (params.length <= 0 || params.length > 1024 || null == params) {
            return "";
        }
        StringBuffer paramString = new StringBuffer("参数:   ");
        for (Object param : params) {
            //将参数转化成字符串
            String s = ToStringBuilder.reflectionToString(param);
            paramString.append(s).append("||");
        }
        return paramString.toString();
    }
    /**
     * 转化request 恳求参数
     * @param paramMap request获取的参数数组
     */
    public static Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<String, String>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }
}

注解完成办法:


package com.**.app.server.annotation;
import com.alibaba.fastjson.JSON;
import com.**.app.server.entity.log.LoggerRecord;
import com.**.app.server.service.LoggerRecordRepository;
import com.**.app.server.util.AopUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
/**
 * Created by cb on 2022/11/15 10:19
 */
@Aspect
@Service
@Slf4j
public class LoggerAdvice {
    @Resource
    private LoggerRecordRepository loggerRecordRepository;
    /**
     * 切点,在注解处切入
     */
    @Pointcut("@annotation(com.safety.app.server.annotation.LoggerManager)")
    public void AroundPointCut(){
    }
    /**
     * 盘绕告诉 @Around  , 当然也能够使用 @Before (前置告诉)  @After (后置告诉)
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("AroundPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            LoggerRecord loggerRecord = saveLog(point, time);
            loggerRecord.setResponseParams(JSON.toJSONString(result));
            loggerRecordRepository.save(loggerRecord);
        } catch (Exception e) {
        }finally {
            return result;
        }
    }
    /**
     * 保存日志
     * @param joinPoint
     * @param time
     */
    private LoggerRecord saveLog(ProceedingJoinPoint joinPoint, long time) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //获取该切点处的办法
        Method method = AopUtil.getMethod(joinPoint);
        LoggerRecord loggerRecord = new LoggerRecord();
        //获取切点处的注解
        LoggerManager loggerManager = method.getAnnotation(LoggerManager.class);
        if (null == loggerManager) {
            return loggerRecord;
        }
        //获取恳求的描绘
//        String description = loggerManager.
        //获取恳求包名
        Signature signature = joinPoint.getSignature();
        //获取恳求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取恳求的办法名
        String methodName = method.getName();
        //获取恳求的参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, String> stringStringMap = AopUtil.converMap(parameterMap);
        log.info("履行===" + methodName + "===开端");
        //打印办法名
        log.info("办法名:" + signature.toString());
        //打印办法参数
        log.info("办法参数:" + JSON.toJSONString(stringStringMap));
        log.info("履行===" + methodName + "===完毕");
        //将日志保存
//        loggerRecord.setDescription(description);
        loggerRecord.setPackageName(signature.toString());
        loggerRecord.setClassName(className);
        loggerRecord.setMethodName(methodName);
        loggerRecord.setRunningTime(time);
        loggerRecord.setRequestParams(JSON.toJSONString(stringStringMap));
        return loggerRecord;
    }
}

最终只需求在自己的接口上加注解@LoggerManager就能够完成此办法的入参出参日志保存

/**
 * 测试
 */
@GetMapping("/test")
@ResponseBody
@LoggerManager
public ResponseResult test(@RequestParam("userName") String userName) throws BusinessException {
    Map<String, Object> verifyCodeMap = VerifyCodeUtils.verifyCode();
    redisUtils.set(VERIFY_CODE + userName, verifyCodeMap, 60);
    return new ResponseResult(verifyCodeMap);
}
保存数据库后的图片:
![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d1cc7a0a83a47299d84f4901bdb33c2~tplv-k3u1fbpfcp-watermark.image?)

注:一切‘**’的地方为自己项目的包名。
好了,下期会改版此日志保存办法,在数据库配置接口名称 就能够完成日志的打印保存。有好的办法也可一同交流,如果有用 给作者点个赞,继续追踪可点个关注 谢谢我们