刚才写完了代码,自测的时候,出现了NPE问题。
排查的时候发现是Lombok的坑,以前也遇到过,所以觉得有必要过来记录一下。
我先描述一下现象,我的代码里面订单服务A 需要调用缓存服务B,服务B就是一个Bean,使用方式是这样的:
class ServiceA {
//使用 Lombok 提供的setter
@Setter
private ServiceB bXCacheService;
public Data getData() {
//这里出现了NPE问题
bXCacheService.getSomeThing();
}
}
这个问题使用Lombok 的同学可能有人遇到过,我用的是蚂蚁的Sofa,Spring也是类似的,
先说下bean初始化过程,是通过反射,调用set 方法初始化bean,下面代码是我截取的部分代码:Spring 中的初始化bean方法
public void setValue(final Object object, Object valueToApply) throws Exception {
//获取write方法,实际就是setXXX方法
final Method writeMethod = this.pd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
writeMethod.setAccessible(true);
return null;
}
});
} else {
writeMethod.setAccessible(true);
}
}
final Object value = valueToApply;
if (System.getSecurityManager() != null) {
} else {
// 通过反射 用set 方法注入属性
writeMethod.invoke(getWrappedInstance(), value);
}
}
问题就出在 Sofa 拼接 bean 变量 set 方法的方式,例如:如果我们希望初始化的 bean 名称为 cacheService,那么 Sofa 拼接的 set 方法为 setCacheService,也就是set + 变量(首字母大写+剩余字符)。
但是如果 bean 名称是:tCacheService,bean 首字母小写,第二次字符是大写,那 set 方法就变成了:settCacheService,当第二个字符是大写的时候,set 不会设置变量 t 为大写 T。
但是 Lombok 不是这样,Lombok 的setter注解实现机制,会让 tCacheService 的 setter 方法变成 setTCacheService(), 所以bean初始化的时候会找不到 WriteMethod,bean注入失败,报NPE问题。
解决方法
解决方法要么调整bean的命名方式,不要让第二个字符是大写,要么改变这种变量不使用Lombok 注入,使用Idea / Eclipse 生成的setter 方法。
也算是Lombok 和 Idea 生成 setter 方法的区别,一般框架、中间件更偏向 Idea 的这种set 变量方式。
另一个需要注意的问题
还有一个不只是Lombok 要注意的点,就是boolean 类型的变量严禁使用 is 开头,因为无论是Lombok 还是Idea 默认生成的get 方法都是is打头,丢掉多余的is,set方法去掉is,可能引发非预期的问题,例如变量 boolean isOpen 和 变量 boolean open 变量的get方法名是一样的:isOpen(); set 方法都是 setOpen(boolean isOpen);
private boolean isOpen;
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean open) {
isOpen = open;
}
常规编程规范里面会让返回值是 boolean 变量的方法名以 is开头,但是变量本身不带is。
// 开火开关 -- 集中参数中心配置项
private String fireSwitch;
public boolean isOpenFire() {
return StringUtils.equalsIgnoreCase( "TRUE", fireSwitch);
}
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8