小心Lombok用法中的坑

363次阅读  |  发布于2年以前

刚才写完了代码,自测的时候,出现了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