Java:自定义注解实现日志记录

前言:自定义注解的魅力

在Java开发中,注解(Annotation)是一种强大的元数据形式,它能够为程序元素(如类、方法、变量等)提供附加信息。自定义注解允许我们根据需要创建特定用途的标签,从而实现特定功能,比如日志记录、权限控制等。通过自定义注解来记录日志,可以让我们的代码逻辑更加专注于业务本身,而将日志记录这样的横切关注点从业务代码中分离出来。

目标:实现一个简单的日志记录注解

我们的目标是创建一个名为@SysLog的自定义注解,它可以应用于任何方法上,当这个方法被调用时,自动记录下方法的执行信息,如方法名、参数值和执行时间。为了实现上述目标,我采用的方案是:自定义注解@SysLog与Spring AOP技术结合,根据MyBatis不同操作类型(增删改查)来实现日志记录,将日志信息保存到数据库中。

废话不多说,上代码!

步骤1:定义日志类型枚举

首先,创建一个枚举类LogTypeEnum来定义日志的操作类型,方便后续日志记录的扩展。

public enum LogTypeEnum {
    INSERT("INSERT", "新增"),
    UPDATE("UPDATE", "更新"),
    DELETE("DELETE", "删除"),
    QUERY("QUERY", "查询");

    private String code;
    private String description;

    LogTypeEnum(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }
}
步骤2:创建日志数据库表

我们需要在数据库中创建一个日志表来存放日志信息。

CREATE TABLE `sys_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `operate_type` VARCHAR(2) NOT NULL COMMENT '操作类型代码',
  `operate_content` varchar(999) NOT NULL COMMENT '操作内容',
  `operate_time` datetime NOT NULL COMMENT '操作时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
步骤3:定义实体类SysLog

创建一个SysLog实体类来映射上述数据库表。

@Data
public class SysLog {
    private Long id;
    private String operateType; // 操作类型
    private String operateContent; // 操作内容
    private LocalDateTime operateTime; // 操作时间
}
步骤4:定义Mapper接口

创建SysLogMapper接口来定义MyBatis操作SQL。

public interface SysLogMapper {
    void insertLog(SysLog sysLog);
}

对应的mapper.xml文件(SysLogMapper.xml)中的SQL插入语句:

<insert id="insertLog">
    INSERT INTO sys_log (operate_type, operate_content, operate_time)
    VALUES (#{operateType}, #{operateContent}, #{operateTime})
</insert>
步骤5:定义自定义注解@SysLog

定义@SysLog注解,允许指定操作类型,并提供一个默认值。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {
    // 操作类型,默认为查询
    String value() default "QUERY";
}
步骤6:实现日志记录切面

为了在注解标记的方法执行前后自动记录日志,我们将使用AspectJ(面向切面编程)来拦截这些方法调用。首先,确保你的项目中已经引入了AspectJ相关的依赖。

接下来,创建一个切面类,用于实现日志记录逻辑:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 日志记录切面类
 */
@Aspect
@Component
public class SysLogAspect {
    @Autowired
    private SysLogMapper sysLogMapper;

    /**
     * 环绕通知,用于拦截带有@SysLog注解的方法
     *
     * @param joinPoint 切入点对象
     * @param sysLog    注解对象,包含操作类型信息
     * @return 原方法执行结果
     * @throws Throwable 抛出异常
     */
    @Around("@annotation(sysLog)")
    public Object around(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed(); // 执行原方法
        } catch (Throwable throwable) { // 捕获方法执行中抛出的异常
            handleLog(joinPoint, sysLog, startTime, throwable.getMessage(), true);
            throw throwable;
        }
        handleLog(joinPoint, sysLog, startTime, null, false); // 方法正常执行完毕后的日志处理
        return result;
    }

    /**
     * 处理日志记录逻辑
     *
     * @param joinPoint      切入点对象
     * @param sysLog         注解对象,包含操作类型信息
     * @param startTime      方法开始执行的时间
     * @param errorMsg       错误信息,如果有异常则传入异常信息
     * @param isError        是否为错误日志
     */
    private void handleLog(ProceedingJoinPoint joinPoint, SysLog sysLog, long startTime, String errorMsg, boolean isError) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        LocalDateTime operateTime = LocalDateTime.now();
        String contentBase = String.format("类名:%s,方法名:%s,操作类型:%s", className, methodName, sysLog.value().getDescription());

        String content = isError ? contentBase + ",异常信息:" + errorMsg : contentBase;

        SysLog log = new SysLog();
        log.setOperateType(sysLog.value());
        log.setOperateContent(content);
        log.setOperateTime(operateTime);
        sysLogMapper.insertLog(log);
    }
}

步骤7:在Controller中使用@SysLog

接下来,我们将在一个示例Controller中展示如何使用@SysLog注解。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/users")
    @SysLog(value = LogTypeEnum.QUERY.getCode()) // 使用枚举的getCode()方法获取字符串值
    public String getUsers() {
        // 查询用户逻辑
        return "获取用户列表";
    }

    @PostMapping("/users")
    @SysLog(value = LogTypeEnum.INSERT.getCode())
    public String createUser(User user) {
        // 创建用户逻辑
        return "创建用户成功";
    }

    // 更多操作...
}

总结

通过以上步骤,我们不仅定义了自己的日志记录注解@SysLog,还实现了它的自动拦截逻辑,使得日志记录变得简单而高效。这种方法的优势在于它将日志记录逻辑从业务代码中分离,提高了代码的可读性和可维护性。现在,每当方法被调用,相关日志就会自动被记录下来,无需在业务代码中手动插入日志语句,极大地提升了开发效率和代码的整洁度;并且这种方式保持了代码的灵活性,同时也保留了枚举作为操作类型的标准化定义,便于理解和维护。

希望这篇教程对你有所帮助,动手实践一下,感受自定义注解和AOP带来的便利!

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值