SpringBoot使用AOP方式记录日志入库

大概流程

先设计系统操作日志表结构,代码中实现日志实体类和mapper层,
然后自定义日志注解,提取主要属性作为注解属性,例如,操作行为:注册用户 操作类型:新增 ;
定义日志注解的切面类,是处理日志入库的逻辑实现;最后使用注解测试效果。
以最近项目作为简单示例实现这个功能 为保持代码原汁原味,将繁琐的工具类也贴上,
自己作取舍 ,多余就删掉,少了自己想法填 ,各自的系统数据结构不同,需要根据现有逻辑修改 如果代码不全 可以留言 我来补全

项目背景

Springboot 2.5.8 tkMybatis mysql 8.X

0.导入使用到的依赖

<!-- AOP依赖 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

1.设计日志表 日志实体类代码

 
import java.util.Date;
import javax.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
 
@Getter
@Setter
@ToString
@Table(name = "log_user_operate")
public class LogUserOperate {
    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "JDBC")
    private Integer id;
 
    /**
     * 模块标题
     */
    @Column(name = "title")
    private String title;
 
    /**
     * 业务类型(0其它 1新增 2修改 3删除)
     */
    @Column(name = "business_type")
    private Integer businessType;
 
    /**
     * 方法名称
     */
    @Column(name = "`method`")
    private String method;
 
    /**
     * 操作人
     */
    @Column(name = "operate_user")
    private String operateUser;
 
    /**
     * 操作人姓名
     */
    @Column(name = "operate_name")
    private String operateName;
 
    /**
     * 部门名称
     */
    @Column(name = "dept_name")
    private String deptName;
 
    /**
     * 操作时间
     */
    @Column(name = "operate_time")
    private Date operateTime;
 
    /**
     * 请求URL
     */
    @Column(name = "oper_url")
    private String operUrl;
 
    /**
     * 主机地址
     */
    @Column(name = "oper_ip")
    private String operIp;
 
    /**
     * 请求方式
     */
    @Column(name = "request_method")
    private String requestMethod;
 
    /**
     * 请求参数
     */
    @Column(name = "oper_param")
    private String operParam;
 
    /**
     * 返回结果
     */
    @Column(name = "return_value")
    private String returnValue;
 
    /**
     * 文字描述
     */
    @Column(name = "`operation`")
    private String operation;
 
    /**
     * 操作状态(0正常 1异常)
     */
    @Column(name = "`status`")
    private Integer status = 0;
 
    /**
     * 失败原因
     */
    @Column(name = "error_msg")
    private String errorMsg;
 
    /**
     * 系统名称
     */
    @Column(name = "sys_name")
    private String sysName = "Device_Manage";
 
    /**
     * 响应用时ms
     */
    @Column(name = "cost_time")
    private Long costTime;
}

2.Mapper持久层

Mapper持久层 插入日志需要调用持久层中的insert方法,将日志记录插入到数据库中。

import com.fri.device_management.model.LogUserOperate;
import tk.mybatis.mapper.common.Mapper;
 
public interface LogUserOperateMapper extends Mapper<LogUserOperate> {
}

3.自定义日志注解 Log注解的作用仅用于标识

注解的类型是@interface,就是在interface之前加了个@。
添加@Retention(RetentionPolicy.RUNTIME)指定注解运行时生效。
添加@Target(ElementType.METHOD)指定注解作用在方法上。
【其中businessType 使用了枚举类 也可以直接使用123这样的数值】

 
import com.fri.device_management.common.log.enums.BusinessType;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
 
/**
 * 自定义操作日志记录注解
 * 注解的属性值 就是在使用注解时 可以传的参数
 */
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLog {
 
    /**
     * 模块
     */
    public String title() default "";
 
    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;
 
    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;
}

3.1我这边使用到的枚举类 业务操作类型

/**
 * 业务操作类型
 *
 *
 */
public enum BusinessType
{
    /**
     * 其它
     */
    OTHER,
 
    /**
     * 新增
     */
    INSERT,
 
    /**
     * 修改
     */
    UPDATE,
 
    /**
     * 查询
     */
    QUERY,
 
    /**
     * 删除
     */
    DELETE,
 
    /**
     * 授权
     */
    GRANT,
 
    /**
     * 导出
     */
    EXPORT,
 
    /**
     * 导入
     */
    IMPORT,
 
    /**
     * 强退
     */
    FORCE,
 
    /**
     * 生成代码
     */
    GENCODE,
 
    /**
     * 清空数据
     */
    CLEAN,
}

3.2我这边使用到的枚举类 操作状态类型

/**
 * 操作状态
 */
public enum BusinessStatus
{
    /**
     * 成功
     */
    SUCCESS,
 
    /**
     * 失败
     */
    FAIL,
}

4.切入面处理逻辑 这里是重点 通读这块代码 自己想怎么实现都成

如果不需要记录那么多信息 就用不到这些工具类 其中获取当前登录用户信息,返回数据结果需要根据自己的项目中适配

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fri.device_management.common.log.annotation.UserLog;
import com.fri.device_management.common.log.enums.BusinessStatus;
import com.fri.device_management.common.utils.IpUtils;
import com.fri.device_management.common.utils.ServletUtils;
import com.fri.device_management.common.utils.StringUtils;
import com.fri.device_management.mapper.LogUserOperateMapper;
import com.fri.device_management.mapper.UserMapper;
import com.fri.device_management.model.LogUserOperate;
import com.fri.device_management.model.User;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
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.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
 
/**
 *
 * 切面类
 * 操作日志记录处理详细罗杰
 *
 * 一个切面 对应一个 注解标记切入点
 *
 * 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
 * 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
 * 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
 * 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
 * 目标对象:Target,通知所应用的对象
 *
 *
 */
@Aspect
@Component
@Slf4j
public class UserLogAspect {
 
 
    @Autowired
    private HttpServletRequest request;
 
 
     // 日志入库的mapper
    @Autowired
    private LogUserOperateMapper logUserOperateMapper;
 
    // 通过请求内的信息,获取当前登录用户的个人信息 补充日志的内容
    @Autowired
    private UserMapper userMapper;
 
    /**
     *  配置切入点  自定义注解的包路径
     */
    @Pointcut("@annotation(com.fri.device_management.common.log.annotation.UserLog)")
    public void logPointCut()
    {
    }
 
 
    /**
     * 处理完请求后执行
     *
     * @param proceedingJoinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint  proceedingJoinPoint, Object jsonResult)
    {
        handleLog(proceedingJoinPoint, null, jsonResult);
    }
 
 
    protected void handleLog(final JoinPoint  joinPoint, final Exception e, Object jsonResult)
    {
        try
        {
            // 获得注解
            UserLog controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null)
            {
                return;
            }
 
 
            // 获取当前的登录用户  
            String token = request.getHeader("Authorization");
            User loginUser = userMapper.selectByToken(token);
 
            // *========数据库日志=========*//
//            SysOperLog operLog = new SysOperLog();
            LogUserOperate operateLog = new LogUserOperate();
            operateLog.setSysName("Device-Manage");
            operateLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            operateLog.setDeptName(loginUser.getDepartmentName());
            operateLog.setOperateName(loginUser.getRealName());
            // 请求的地址
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            operateLog.setOperIp(ip);
            // 返回参数
            operateLog.setReturnValue(JSON.toJSONString(jsonResult));
 
            operateLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
            if (e != null)
            {
                operateLog.setStatus(BusinessStatus.FAIL.ordinal());
                operateLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operateLog.setMethod(className + "." + methodName + "()");
 
            // 设置请求方式
            operateLog.setRequestMethod(ServletUtils.getRequest().getMethod());
 
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operateLog);
            // 保存数据库
            logUserOperateMapper.insertSelective(operateLog);
        }
        catch (Exception exp)
        {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }
 
    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, UserLog log, LogUserOperate operLog) throws Exception
    {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
 
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData())
        {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog);
        }
    }
    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, LogUserOperate operLog) throws Exception
    {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod))
        {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(StringUtils.substring(params, 0, 2000));
        }
        else
        {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }
 
    /**
     * 是否存在注解,如果存在就获取
     */
    private UserLog getAnnotationLog(JoinPoint joinPoint) throws Exception
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
 
        if (method != null)
        {
            return method.getAnnotation(UserLog.class);
        }
        return null;
    }
 
    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (!isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }
 
 
    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    public boolean isFilterObject(final Object o)
    {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

4.1用到的工具类IpUtils

import org.springframework.util.StringUtils;
 
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
 
/**
 * 获取IP方法
 *
 *
 */
public class IpUtils
{
    public static String getIpAddr(HttpServletRequest request)
    {
        if (request == null)
        {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("X-Real-IP");
        }
 
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }
 
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
 
    public static boolean internalIp(String ip)
    {
        byte[] addr = textToNumericFormatV4(ip);
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }
 
    private static boolean internalIp(byte[] addr)
    {
        if (addr == null || addr.length < 2)
        {
            return true;
        }
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0)
        {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4)
                {
                    return true;
                }
            case SECTION_5:
                switch (b1)
                {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }
 
    /**
     * 将IPv4地址转换成字节
     *
     * @param text IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String text)
    {
        if (text.length() == 0)
        {
            return null;
        }
 
        byte[] bytes = new byte[4];
        String[] elements = text.split("\\.", -1);
        try
        {
            long l;
            int i;
            switch (elements.length)
            {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L))
                        return null;
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L))
                        return null;
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L))
                        return null;
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    for (i = 0; i < 2; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L))
                        return null;
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    for (i = 0; i < 4; ++i)
                    {
                        l = Integer.parseInt(elements[i]);
                        if ((l < 0L) || (l > 255L))
                            return null;
                        bytes[i] = (byte) (int) (l & 0xFF);
                    }
                    break;
                default:
                    return null;
            }
        }
        catch (NumberFormatException e)
        {
            return null;
        }
        return bytes;
    }
 
    public static String getHostIp()
    {
        try
        {
            return InetAddress.getLocalHost().getHostAddress();
        }
        catch (UnknownHostException e)
        {
        }
        return "127.0.0.1";
    }
 
    public static String getHostName()
    {
        try
        {
            return InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException e)
        {
        }
        return "未知";
    }
}

4.2 用到的工具类ServletUtils

 
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
 
import cn.hutool.core.convert.Convert;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
 
/**
 * 客户端工具类
 *
 *
 */
public class ServletUtils {
    /**
     * 获取String参数
     */
    public static String getParameter(String name) {
        return getRequest().getParameter(name);
    }
 
    /**
     * 获取String参数
     */
    public static String getParameter(String name, String defaultValue) {
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
    }
 
    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name) {
        return Convert.toInt(getRequest().getParameter(name));
    }
 
    /**
     * 获取Integer参数
     */
    public static Integer getParameterToInt(String name, Integer defaultValue) {
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
    }
 
    /**
     * 获取request
     */
    public static HttpServletRequest getRequest() {
        return getRequestAttributes().getRequest();
    }
 
    /**
     * 获取response
     */
    public static HttpServletResponse getResponse() {
        return getRequestAttributes().getResponse();
    }
 
    /**
     * 获取session
     */
    public static HttpSession getSession() {
        return getRequest().getSession();
    }
 
    public static ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
 
    /**
     * 将字符串渲染到客户端
     *
     * @param response 渲染对象
     * @param string   待渲染的字符串
     * @return null
     */
    public static String renderString(HttpServletResponse response, String string) {
        try {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 是否是Ajax异步请求
     *
     * @param request
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String accept = request.getHeader("accept");
        if (accept != null && accept.indexOf("application/json") != -1) {
            return true;
        }
 
        String xRequestedWith = request.getHeader("X-Requested-With");
        if (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1) {
            return true;
        }
 
        String uri = request.getRequestURI();
        if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
            return true;
        }
 
        String ajax = request.getParameter("__ajax");
        if (StringUtils.inStringIgnoreCase(ajax, "json", "xml")) {
            return true;
        }
        return false;
    }
}

4.3 用到的工具类StringUtils

 
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
/**
 * 字符串工具类
 *
 *
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils
{
    /** 空字符串 */
    private static final String NULLSTR = "";
 
    /** 下划线 */
    private static final char SEPARATOR = '_';
 
    /**
     * 获取参数不为空值
     *
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue)
    {
        return value != null ? value : defaultValue;
    }
 
    /**
     * * 判断一个Collection是否为空, 包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }
 
    /**
     * * 判断一个Collection是否非空,包含List,Set,Queue
     *
     * @param coll 要判断的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }
 
    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     ** @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }
 
    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }
 
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }
 
    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }
 
    /**
     * * 判断一个字符串是否为空串
     *
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }
 
    /**
     * * 判断一个字符串是否为非空串
     *
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }
 
    /**
     * * 判断一个对象是否为空
     *
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }
 
    /**
     * * 判断一个对象是否非空
     *
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }
 
    /**
     * * 判断一个对象是否是数组类型(Java基本型别的数组)
     *
     * @param object 对象
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }
 
    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }
 
    /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }
 
        if (start < 0)
        {
            start = str.length() + start;
        }
 
        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }
 
        return str.substring(start);
    }
 
    /**
     * 截取字符串
     *
     * @param str 字符串
     * @param start 开始
     * @param end 结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }
 
        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }
 
        if (end > str.length())
        {
            end = str.length();
        }
 
        if (start > end)
        {
            return NULLSTR;
        }
 
        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }
 
        return str.substring(start, end);
    }
 
 
 
    /**
     * 字符串转set
     *
     * @param str 字符串
     * @param sep 分隔符
     * @return set集合
     */
    public static final Set<String> str2Set(String str, String sep)
    {
        return new HashSet<String>(str2List(str, sep, true, false));
    }
 
    /**
     * 字符串转list
     *
     * @param str 字符串
     * @param sep 分隔符
     * @param filterBlank 过滤纯空白
     * @param trim 去掉首尾空白
     * @return list集合
     */
    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
    {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(str))
        {
            return list;
        }
 
        // 过滤空白字符串
        if (filterBlank && StringUtils.isBlank(str))
        {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split)
        {
            if (filterBlank && StringUtils.isBlank(string))
            {
                continue;
            }
            if (trim)
            {
                string = string.trim();
            }
            list.add(string);
        }
 
        return list;
    }
 
    /**
     * 下划线转驼峰命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }
 
            curreCharIsUpperCase = Character.isUpperCase(c);
 
            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }
 
            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }
 
        return sb.toString();
    }
 
    /**
     * 是否包含字符串
     *
     * @param str 验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }
 
    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty())
        {
            // 没必要转换
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划线,仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }
 
    /**
     * 驼峰式命名法 例如:user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);
 
            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

5.在Controller使用实例 @UserLog

@UserLog(title = "新增设备",businessType = BusinessType.INSERT)
@PostMapping("add")
public Result insertVehicle( Device device) {
     Result resultJson = new  Result();
     try {
         resultJson.setSuccessCode().setData(deviceService.insertDevice(device));
     }catch (Exception e){
         e.printStackTrace();
         resultJson.setErrorCode().setMessage(e.getMessage());
     }
    return resultJson;
}

耐心通读代码,写出你想要的日志存储机制

posted @ 2024-12-10 09:45  西奥-xiao  阅读(265)  评论(0)    收藏  举报