Loading...

本篇文章采用 Spring AOP 和自定义日志注解实现了日志的保存。

# 引入需要的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

# 创建日志相关类

日志实体类

LogEntity

package com.example.demo.entity;
import java.time.LocalDateTime;
public class LogEntity {
    /**
     * uuid
     */
    private String id;
    /**
     * 方法名
     */
    private String method;
    /**
     * 操作类型
     */
    private String operationType;
    /**
     * 参数
     */
    private String params;
    /**
     * 操作消息(成功 / 失败)
     */
    private String message;
    /**
     * 操作时间
     */
    private LocalDateTime operationDate;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getMethod() {
        return method;
    }
    public void setMethod(String method) {
        this.method = method;
    }
    public String getOperationType() {
        return operationType;
    }
    public void setOperationType(String operationType) {
        this.operationType = operationType;
    }
    public String getParams() {
        return params;
    }
    public void setParams(String params) {
        this.params = params;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public LocalDateTime getOperationDate() {
        return operationDate;
    }
    public void setOperationDate(LocalDateTime operationDate) {
        this.operationDate = operationDate;
    }
}

日志存储层

LogDao

package com.example.demo.dao;
import com.example.demo.entity.LogEntity;
import com.example.demo.log.Log;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Component
public class LogDao {
    //TODO 为了方便采用临时存储(可连接数据库)
    private static final Map<String, LogEntity> LOGS = new HashMap<>();
    public LogEntity save(LogEntity logEntity){
        logEntity.setId(UUID.randomUUID().toString());
        LOGS.put(logEntity.getId(), logEntity);
        return logEntity;
    }
}

转化工具类

ConvertUtils

package com.example.demo.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.ValidationException;
public class ConvertUtils {
    public static final Logger LOG = LoggerFactory.getLogger(ConvertUtils.class);
    private static ObjectMapper objectMapper;
    public static ObjectMapper getObjectMapper() {
        if (objectMapper == null) {
            objectMapper = new ObjectMapper();
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            objectMapper.registerModule(new JavaTimeModule());
        }
        return objectMapper;
    }
    public static String toJson(Object object) {
        String json = null;
        try {
            json = getObjectMapper().writeValueAsString(object);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            LOG.error("序列化失败:" , e);
            throw new ValidationException("序列化失败");
        }
        return json;
    }
}

# 自定义日志注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Log {
    
    // 操作类型
    String operationType() default "";
    
    // TODO 可拓展其他参数
}

# 创建 AOP 切面实现类

package com.example.demo.log;
import com.example.demo.dao.LogDao;
import com.example.demo.entity.LogEntity;
import com.example.demo.util.ConvertUtils;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
@Aspect
@Component(value = "logAspect")
public class LogAspect {
    private static final Logger LOG = LoggerFactory.getLogger(LogAspect.class);
    private static final String POST = "POST";
    private static final String GET = "GET";
    @Autowired
    private LogDao logDao;
    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.example.demo.log.Log)")
    public void logPointCut() {
    }
    /**
     * 前置通知
     */
    @Before(value = "logPointCut()")
    public void before(JoinPoint joinPoint) {
        doBefore(joinPoint);
    }
    /**
     * 返回通知,在方法返回结果之后执行
     */
    @AfterReturning(pointcut = "logPointCut()")
    public void afterReturning(JoinPoint joinPoint) {
        doAfter(joinPoint, null);
    }
    /**
     * 异常通知,在方法抛出异常之后执行
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        doAfter(joinPoint, e);
    }
    private void doBefore(JoinPoint joinPoint) {
        LOG.info("前置操作开始...");
        try {
            // 打印参数
            printParams(joinPoint);
        } catch (Exception e) {
            // 打印异常日志
            LOG.error("前置通知异常:" + e.getMessage());
        }
        LOG.info("前置操作结束...");
    }
    private void doAfter(JoinPoint joinPoint, Exception e) {
        LOG.info("后置操作开始...");
        LogEntity logEntity = new LogEntity();
        try {
            // 得到注解
            Log log = getAnnotation(joinPoint);
            if (log == null) {
                return;
            }
            LOG.info("日志保存开始...");
            // 类名
            String className = joinPoint.getTarget().getClass().getName();
            // 方法名
            String methodName = joinPoint.getSignature().getName();
            // 操作类型
            String operatorType = log.operationType();
            logEntity.setMethod(className + "." + methodName);
            logEntity.setOperationType(operatorType);
            logEntity.setParams(ConvertUtils.toJson(joinPoint.getArgs()));
            logEntity.setMessage(null == e ? "处理成功" : "处理失败:" + e.getMessage());
            logEntity.setOperationDate(LocalDateTime.now());
            LOG.info(ConvertUtils.toJson(logEntity));
        } catch (Exception exception) {
            // 记录本地异常日志
            LOG.error("后置通知异常:" + exception.getMessage());
            logEntity.setMessage("处理失败:" + exception);
        } finally {
            logDao.save(logEntity);
            LOG.info("日志保存结束...");
        }
        LOG.info("后置操作结束...");
    }
    private void printParams(JoinPoint joinPoint) {
        //request 请求
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        // 请求 url
        String url = request.getRequestURL().toString();
        // 请求方式
        String method = request.getMethod();
        // 请求参数
        String queryString = request.getQueryString();
        Object[] args = joinPoint.getArgs();
        StringBuilder requestParams = new StringBuilder("请求参数为:{");
        if (args.length > 0) {
            if (POST.equals(method)) {
                requestParams.append(Arrays.asList(args)).append("}");
            } else if (GET.equals(method)) {
                String params = URLDecoder.decode(queryString, StandardCharsets.UTF_8);
                requestParams.append(Arrays.toString(params.split("&"))).append("}");
            }
        }
        LOG.info(requestParams.toString());
    }
    private Log getAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            // 拿到自定义注解的信息
            return method.getAnnotation(Log.class);
        }
        return null;
    }
}

# 测试

# 定义一个测试实体

DemoEntity

package com.example.demo.entity;
import java.io.Serializable;
public class DemoEntity implements Serializable {
    private String id;
    private String name;
    private String age;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "DemoEntity{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}

# 定义一个测试控制层

package com.example.demo.controller;
import com.example.demo.entity.DemoEntity;
import com.example.demo.log.Log;
import com.example.demo.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
    @Autowired
    private DemoService demoService;
    
    @RequestMapping(value = "/submit")
    @Log(operationType = "提交")
    public String submit(@RequestBody DemoEntity demoEntity){
        return demoEntity.toString();
    }
}

# 发起请求测试

localhost:8080/submit

请求参数

{
    "id": "123456",
    "name": "测试",
    "age": "18"
}

日志打印

2022-05-31 13:40:20.140 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 前置操作开始...
2022-05-31 13:40:20.140 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 请求参数为:{[DemoEntity {id='123456', name=' 测试 ', age='18'}]}
2022-05-31 13:40:20.140 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 前置操作结束...
2022-05-31 13:40:20.141 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 后置操作开始...
2022-05-31 13:40:20.141 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 日志保存开始...
2022-05-31 13:40:20.141 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : {"id":null,"method":"com.example.demo.controller.DemoController.submit","operationType":"提交","params":"[{"id":"123456","name":" 测试 ","age":"18"}]","message":"处理成功","operationDate":[2022,5,31,13,40,20,141891900]}
2022-05-31 13:40:20.141 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 日志保存结束...
2022-05-31 13:40:20.141 INFO 20340 --- [nio-1111-exec-2] com.example.demo.log.LogAspect : 后置操作结束...

更新于

请我喝[茶]~( ̄▽ ̄)~*

七音 微信支付

微信支付

七音 支付宝

支付宝