工作错题录

记录一些在工作中出现的不应该或少见的错误,如 Java 基础、Spring 基础等常识问题以及生僻的知识,用于笔记,也用于警示。


一、Java

1. Long 等数字类型比较

1.1 问题描述:在进行两个 Long 对象比较时,直接用了“==”,如:a.id==b.id,经过简单测试没问题,然后就提交了,使用一段时间后,发现出现问题,一样的值结果是 false;

1.2 处理过程:第一感觉就是不该用“==”,一般情况下都是 Long 对象和 long 值比较,如a.id=1,会发生自动拆箱,是没有问题的;左右都是包装类型的时候直接双等于肯定是错的,但是困惑的是,测试的时候为什么是对的,经过网上一番查询,才知道 127 是个分界岭;

1.3 问题说明:Long 中有一个静态的内部类 LongCache,缓存了 -128 至 127 之间的值,如果传入的值在此范围内,则直接从缓存中返回引用,否则新建 Long 实例;因此,超过 127,双等于就错了;

1.4 正确方法,推荐优先级由上至下:

  • equals()方法,如:a.equals(b),看源码可知,经过重写,会比较对象的值;

  • longValue()方法,如:a.longValue()==b.longValue(),先拆箱,再比较;

  • compareTo()方法,如:a.compareTo(b)==0,比较大小,返回差值;

    1.5 扩展:Byte、Short、Integer、Long 等整形数字都是一样的情况;Float、Double 两个浮点类型,没有内部静态缓存,比较方法一样。

二、SpringBoot

1. 配置文件读取

1.1 问题描述:

  • 一般情况下,会在配置文件里 application.yml 定义自定义配置项,如下:

    1
    2
    3
    4
    # 项目配置
    custom:
    # 名称
    name: test
  • 读取配置属性,定义对应的配置 JavaBean,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    @ConfigurationProperties(prefix = "custom")
    public class CustomConfig {
    /**
    * 名称
    */
    private static String name;

    // get、set 方法省略
    }
  • 情形说明:

    • 这一套代码在项目里已经正常使用,这天,要加个新属性;
    • 很简单的操作,先在配置文件里加配置属性,再在类里加属性和 set/get 方法;然后提交了,告别人加完了;这么简单,还用测试吗?
    • 打脸来的很快,同事说获取到的属性是 null;尴尬,自己写个单元测试,确实取不到值;

1.2 问题解决:

  • 看来看去、改来改去都没效果,set/get 方法也没缺少,也没发现框架里有什么特殊处理;

  • 经过仔细比对,发现 set 方法是 static 的,是唯一和其他属性不一致的地方,好吧,修改后再测试,取到了;

  • 问题就出在 set 方法是静态的,因为属性是静态的,IDEA 生成 set/get 方法自然也是静态的,没意识到有什么问题;

  • 正确写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private static String name;

    public static String getName() {
    return name;
    }
    // 重点注意去掉 static 修饰符
    public void setName(String name) {
    RuoYiConfig.name = name;
    }

1.3 问题解释:

  • 经网友提醒,根据 Springboot 源码,@ConfigurationProperties 只会调用非静态的 set 方法;
  • @ConfigurationProperties 会根据配置文件的属性调用对应的 set 方法;
  • 切记:prefix 值必须全部为小写,set 方法必须为 public 且非 static;

三、Thymeleaf

1. th:if 标签

1.1 问题描述:

  • 原使用场景是,当属性不为空时,div 存在,反之,div 不存在。
  • 但是,按照语法写来写去,结果都不对。要不然就是一直出现,要不然就是一直不出现。
  • 刚开始以为是语法不对,试了几种,都是一样的结果。
  • 后灵光一现,猜测错误原因并验证,最终解决。

1.2 正确写法:

1
2
3
4
5
6
7
8
<form class="form-horizontal m" id="form-equipment-edit" th:object="${equipment}">
<div class="form-group" th:if="${not #strings.isEmpty(equipment.userManualShowName)}">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-7">
<input th:field="*{userManualShowName}" class="form-control" type="text" readonly/>
</div>
</div>
</form>

1.3 说明:

  • 关键在于 th:if="${not #strings.isEmpty(equipment.userManualShowName)}" 里的equipment.userManualShowName
  • 原错误写法:userManualShowName。因为在 form 中已经绑定了对象th:object="${equipment}",所以里面的 input 中可以直接访问属性th:field="*{userManualShowName}"。自然在写 if 标签时也用了同样写法。
  • 但是,form 绑定的对象和 div 是没关系的,所以 div 访问还是需要通过对象访问属性,即equipment.userManualShowName

2. th:data 事件传参

1.1 情景描述:
在详情页面中出现点击操作,可能需要将后台的值绑定并当作参数传入点击事件。
1.2 正确写法:

1
<a th:data-umr="${equipment.userManualRealName}" onclick="downloadFile(this.getAttribute('data-umr'))">[[${equipment.userManualShowName}]]</a>

1.3 说明:
通过 th:data- 自定义属性接收参数值,再通过 this.getAttribute('data-umr') 获取。