起因
最近在写CRUD
的时候,发现有个分页的VO
写的健壮性比较差,一时手痒改了一下,没想到改了之后好几个功能都出现了问题。
原VO
关键代码如下:
public class PageVo implements Serializable{
// ...省略所有无关代码
Map<String, String> query
}
这个VO
是用于从前端分页查询时传参,而query
是用于传递查询条件的(这里不讨论用Map
传参是否合理)。当前端无查询条件时则会导致query
为null
,如果不注意容易出现NPE
。
所以我就改造成下面这样了。
public class PageVo implements Serializable{
// ...省略所有无关代码
Map<String, String> query=new HashMap<>
}
但是没想到就是这么简单的改造居然都翻车(・ε・`)
没办法,只好去排查问题。
发现问题
想过很多种原因,但是我真没想到居然是因为这样(/‵Д′)/~ ╧╧,不多说了,问题关键代码如下:
if (StringUtils.isEmpty(page.getQuery())) {
// 省略处理逻辑
}
居然用StringUtils
去判断一个Map
是否为空,好歹也换个CollectionUtils
啊(╬ ̄皿 ̄)凸
虽然是前人挖坑,但是为什么Spring
的`StringUtils
居然设计成支持Object
入参呢o_o ....
想了一下,还是去看看源码吧
源码分析
StringUtils
的isEmpty()
方法源码超级简单,三行代码搞定(其实严格来说就一行代码):
public static boolean isEmpty(@Nullable Object str) {
return (str == null || "".equals(str));
}
既然我的Map
对象不为null
,那么问题应该是因为String
的equals()
方法。不多说,继续跟踪源码
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
// 问题出在这里
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
这个equals()
方法的逻辑很简单
- 入参为
null
则返回true
- 入参不为
String
类型返回false
- 入参对象和
this
对象都为String
就比较它们内置的char[]
数组长度和每个char
元素是否相同,满足则返回true
,否则返回false
而我的问题就是由第二点引起的,因为类型不相同┴─┴︵╰(‵□′╰)
教训总结
- 不建议使用
Spring
的StringUtils
的isEmpty()
对非String
类型的对象判空。(这里建议换成apache common
的StringUtils
或者Google Guava
的Strings
,这两个工具包都是类型强约束的) - 无论是修改哪处的代码都最好检查一下引用,避免修复小问题引出大问题
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。