• 使用AOP进行日志记录


    需求:我们需要对用户账号进行审批,停用或启用,并且审批时我们需要对指定接口进行日志记录,记录到一个表内,包括记录编号和操作内容以备注

    • 用户表
    -- auto-generated definition
    create table t_user
    (
        id       bigint auto_increment primary key,
        username varchar(100)  null,
        password varchar(100)  null,
        status   int default 0 null comment '1/0 停用/启用'
    );
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 操作记录表sql
    create table audit_log
    (
        id            bigint auto_increment comment 'id' primary key,
        create_time   timestamp default CURRENT_TIMESTAMP null,
        update_time   timestamp default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
        audit_no      varchar(255)                        null comment '操作编号',
        audit_content varchar(255)                        not null comment '操作内容',
        remark        varchar(255)                        null comment '备注'
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们现在的需求是将内容记录到该日志表内,所以我们需要记录的参数包括:操作编号、操作内容和备注,其他内容会自动生成。我们可以用AOP完成这个事情

    首先我们每次操作时,需要知道用户的编号、我们需要操作的备注、内容、和操作的状态是停用或者还是启用,操作内容以及操作的状态和接口相关,所以我们只需要关心编号和备注。

    因为操作的内容和状态都是与接口有关,所以我们将他作为注解实现

    • LogAudit
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface LogAudit {
        /**
         * 操作 1/0  停用/启用
         * @return  int
         */
        int op() default 1;
    
        /**
         * 操作内容
         * @return pass
         */
        String content() default "PASS";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    因为不同的接口我们需要的参数不同,但是都需要操作的编号和备注,这些是前端传进来的内容,所以我们将编号和备注抽出来。

    • BaseDTO
    @Data
    @ApiModel("基础dto")
    public class BaseDTO {
        @ApiModelProperty("编号")
        private String auditNo;
    
        @ApiModelProperty("备注")
        private String remark;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 接着,我们传递需要的用户DTO
    @Data
    @ApiModel
    public class UserDTO extends BaseDTO {
        private String username;
      
        private Integer status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • service
    public interface UserService extends IService<User> {
    
        void auditUser(UserDTO user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • impl
    @Service
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
        @Override
        @LogAudit(op = 0,content = "START")
        @Transactional
        public void auditUser(UserDTO user) {
    				LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(User::getStatus,user.getStatus()).
                    eq(User::getUsername,user.getUsername());
            update(wrapper);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 完成之后我们做我们的AOP增强,用来获取参数以及注解中的内容,进行封装
    /**
     * @date 2023/9/13 15:42
     */
    @Component
    @Aspect
    @Slf4j
    public class LogAspect {
    
        @Autowired
        private AuditLogMapper logMapper;
    
        @Autowired
        private TransactionTemplate transactionTemplate;
    
        @Pointcut("@annotation(com.example.annotation.LogAudit)")
        private void pointCut(){};
    
        //后置切面
        @After(value = "pointCut()")
        @Transactional
        public void insertLog(JoinPoint joinPoint){
          
           //获取参数列表
            Object[] args = joinPoint.getArgs();
            AuditLog auditLog = new AuditLog();
            for (Object arg : args) {
                //	需要定位到有所需内容的参数处
                if(arg instanceof BaseDTO){
                    BaseDTO baseDTO = (BaseDTO) arg;
                    String auditNo = baseDTO.getAuditNo();
                    String remark = baseDTO.getRemark();
                    auditLog.setAuditNo(auditNo);
                    auditLog.setRemark(remark);
                    break;
                }
            }
            //获取注解对象 填充属性
            LogAudit annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LogAudit.class);
            int op = annotation.op();
            auditLog.setAuditContent("操作::"+op+",类型:"+annotation.content());
            auditLog.setCreateTime(new Date());
            auditLog.setUpdateTime(new Date());
            //默认传播行为,可以指定
            transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            //声明式事物会失效,这里用编程式事物
            transactionTemplate.execute(t->{
                logMapper.insert(auditLog);
                return Boolean.TRUE;
            });
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    我们写一个测试类

        @Autowired
        private UserService userService; 
    
        @Test
        public void aopTest(){
            UserDTO userDTO = new UserDTO();
            userDTO.setUsername("aaaa");
            userDTO.setAuditNo("NO0000000029");
            userDTO.setStatus(0);
            userDTO.setRemark("表现良好,再接再厉");
            userService.auditUser(userDTO);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    查看控制台日志

    JDBC Connection [HikariProxyConnection@1486862157 wrapping com.mysql.cj.jdbc.ConnectionImpl@200d1a3d] will be managed by Spring
    ==>  Preparing: UPDATE t_user SET status=? WHERE (username = ?) 
    ==> Parameters: 0(Integer), aaaa(String)
    <==    Updates: 1
    Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@45e140ae]
    Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@45e140ae] from current transaction
    ==>  Preparing: INSERT INTO audit_log ( create_time, update_time, audit_no, audit_content, remark ) VALUES ( ?, ?, ?, ?, ? ) 
    ==> Parameters: 2023-09-13 17:57:11.266(Timestamp), 2023-09-13 17:57:11.266(Timestamp), NO0000000029(String), 操作::0,类型:START(String), 表现良好,再接再厉(String)
    <==    Updates: 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    数据库也是有内容的。

    当然我们也可以使用@AfterReturning指定只有操作成功后才记录日志。

  • 相关阅读:
    python提取网页指定内容
    Java中如何正确的将byte[]数组转化为String类型?
    IAP固件升级进阶(Qt上位机)
    @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @AllArgsConstructor
    电力系统规划学习笔记(长期更新)
    宝塔node建立软连接,解决command not found问题
    python - 闭包与装饰器
    阿里云OSS前端直传+net core后端签名
    java中的深度复制和浅复制的BUG
    旧版本金庸群侠传3D新Unity重置修复版入门-lua”脚本“
  • 原文地址:https://blog.csdn.net/YSecret_Y/article/details/132860693