本文正在参加「金石计划」

前言

在日常开发的过程中,一般咱们需求先看需求文档,根据需求文档的需求进行开发,其实在咱们了解业务的时分,脑筋就现已在构建代码如何写了,作业几年后或许一些常见的400、404、500、503等一些报错应该会很少呈现,或许会呈现一些看似正常,但实际业务接口回来有问题的状况,跟着咱们业务逻辑的复杂性,这些潜存的BUG或许会躲藏的很深,在某种程度上或许会迷惑开发人员、测验人员,从而让BUG溜到出产。

关于bug排查

  1. 首要先借住代码标准东西,能够根据idea的Alibaba Java Coding Guidelines阿里的代码标准,看看代码有没有爆黄之处,能够先从这个点看看逻辑是否有问题,之前公司还会用到checkStyle,自己设定了一套标准只需求传入checkStyle.xml进行配置即可,也能够查看是不是因为代码标准带来的bug
  2. 针对于某一个接口,逐行的去看代码,这儿假如你是作业几年后对业务代码有所理解,能够去看到代码,你在看代码的过程中脑子或许就现已在构建没行代码的回来值,每个if判别会呈现的状况,对这个成果进行排查。
  3. 假如你刚作业没几年, 那么第二条能够不用去考虑,直接拿到入参,根据postman去恳求,然后debug代码一行一行的看,详细看每个对象在履行的过程中里面特点的值的变化,这种方法也是最保险的排查方法,关于代码我只相信履行成果,即便是现在我也不会嫌费事,仍是去拿到接口入参去debug调试,这种方法我认为是最准确也是比较保险的bug排查方法。
  4. 接口中打日志,接口恳求前期进行logger打日志,对入参出参进行日志记载,在业务相对复杂履行成果处进行日志记载,包含一些状况(status)、标识(flag)、list对象和map调集进行stream函数操作、包含过滤、计算、格式转化后的成果,进行日志记载。

关于呈现的bug

入参指针问题

对入参数进行stream函数操作,最终还使用本来改变过的参数进行业务处理,导致数据会呈现重复或许缺失。

    /**
     * 创立用户
     * @param userDTOS
     * @return
     */
    @Override
    public boolean createUser(List<UserDTO> userDTOS) {
        List<SysUser> sysUsers = Lists.newArrayList();
        check(userDTOS,sysUsers);
        return true;
    }
    /**
     * 校验逻辑
     * @param userDTOList
     * @param sysUsers
     */
    private void check(List<UserDTO> userDTOList, List<SysUser> sysUsers) {
//        List<UserDTO> userDTOS = Lists.newArrayList();
//        List<UserDTO> updateUserDTOS = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(userDTOList)) {
            //过滤出需求新增的数据
            userDTOList = userDTOList.stream().filter(r -> r.getActionType() != null).collect(Collectors.toList());
            List<SysUser> sysUserList = BeanCopyUtil.copyListProperties(userDTOList, SysUser::new);
            //过滤出需求更新的数据
            userDTOList = userDTOList.stream().filter(r -> r.getActionType() == null && !StringUtils.isEmpty(r.getTag())).collect(Collectors.toList());
            List<SysUser> updateSysUserList = BeanCopyUtil.copyListProperties(userDTOList, SysUser::new);
            //数据更新  逻辑省掉
            //数据新增  逻辑省掉
            //组装数据
            sysUsers.addAll(sysUserList);
            sysUsers.addAll(updateSysUserList);
            log.info("用户信息数据组装回来成果{}", JSON.toJSONString(sysUsers));
        }
    }

运行成果

这儿能够看到,咱们的入参是一个List对象,包含三条数据。

比较容易忽视的几个BUG

这儿能够看到,咱们通过第一次过滤,过滤出来了两条数据。

比较容易忽视的几个BUG

再次过滤的时分,发现更新数据没有过滤出来,是因为咱们拿现已过滤出来的数据进行了二次过滤,导致不会存在对应的数据,是因为咱们拿过滤数据的参数和接受过滤成果的参数,用的是同一个List对象,这样指向就会呈现问题,导致咱们漏掉了一条数据。

比较容易忽视的几个BUG

这种状况在业务代码许多很复杂的状况下,真的很简单呈现,让人目炫导致写错,呈现bug,需求从头声明List对象进行过滤成果的接纳,这样才不会漏数据,这也是写代码习惯的问题,⚠️我在查看代码的时分的确看到过有人这么写过导致呈现数据缺失,呈现bug。

比较容易忽视的几个BUG

Mybatis的隐式转化

前端通过字符串类型传值,传了字符串为“0”的参数。

比较容易忽视的几个BUG

这儿能够看到,Mybatis现已将字符串类型转为Integer类型了,

比较容易忽视的几个BUG

这时分在mapper.xml里对这个入参,假如进行了!=’‘ 判别,由于入参现已被转为Integer类型,在查询的时分这个参数就不会被拼接到sql后边,导致查询数据呈现问题。

比较容易忽视的几个BUG

业务接口未提交前的影响

一个业务的接口,还没履行完之前,通过查询,然后更新某个状况,然后在进行查询。

@Override
@Transactional(rollbackFor = Exception.class)
public Boolean transactionCommit(String userName) {
    //查询用户
    SysUser sysUser = userMapper.selectUserByUserName(userName,null);
    if (null != sysUser) {
        //用户信息状况更新 status更新为1
        userMapper.updateStatus(userName);
    }
    //再次查询
    SysUser sysUser1 = userMapper.selectUserByUserName(userName,"1");
    log.info("状况为1的用户信息"+JSON.toJSONString(sysUser1));
    return  true;
}

这段代码我通过debug模式断点调试,这儿能够看到我现已将user表里的status的状况更新为“1”,并且在业务内进行查询,也是能够查询到状况为“1”的数据,可是我通过在数据库履行sql,查询到的仍是status为”0“的数据

比较容易忽视的几个BUG

比较容易忽视的几个BUG

这时分我走完debug提交完业务,这儿status的状况变为“1”,由此可见mysql的隔离级别是可重复读的,可是这个时分呈现时间差的时分会呈现幻读,所以在某种程度上咱们的接口还没履行完,测验在测验阶段进行数据库查询数据,或许并不是他所想查询到的数据。

总结

这些是最近项目中呈现的一些bug贴出来与咱们共享,或许也是咱们写代码会忽略的一些问题,有时分需求很紧一个接着一个来,咱们在写代码的时分,或许会为了赶进度有些代码写出来,是没有通过思考的,导致bug的呈现,咱们需求在寻求新技术的一起,coding好自己手头上作业的代码,即便是一些枯燥无味的CRUD,也是一种锻炼。