EasyExcel导入时invoke方法中data对象取不到值问题

【饿了么、美团】外卖红包领取地址 >> 能省一点是一点

在后台项目中,经常需要使用excel进行数据的导入,我们可以使用阿里巴巴的EasyExcel快速实现。

在一次需求开发中发现,使用EasyExcel导入时,在invoke方法中data对象的属性值一直取不到,全是null值,非常的疑惑。经过排查,发现其实是EasyExcel和Lombok结合使用导致的问题,或者说是cglib中BeanMap和Lombok结合的问题。(解决办法直接拉到文末

发生属性值取不到的原因是:
1、在对象上使用了@Data注解
Lombok会默认帮对象生成set方法,这里没有问题,但是Lombok为了方便链式的调用set,生成的是带返回值的set方法,而非正常的void的set方法。

2、EasyExcel中,对invoke方法中的data对象的赋值,是使用的cglib中BeanMap类实现的
赋值的代码在ModelBuildEventListener这个类中,查看buildUserModel方法的倒二行

BeanMap.create(resultModel).putAll(map);

3、cglib中的BeanMap只会对普通的返回值为void的set方法有效,而带返回值的set方法会被忽略掉。

正是由于这一点,所以导致上述问题的产生。通过查看BeanMap的源码,最后使用的是:

abstract public Object put(Object bean, Object key, Object value);

这么一个抽象方法,具体的实现看不到(应该是cglib代理实现了对应的实现,暂不知道如何进行查看)
不过经过万能的百度,排查到,相关的代码位于:

java.beans.Introspector#getTargetPropertyInfo

方法中的

if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
    pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
} else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
    // Simple setter
    pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
    if (throwsException(method, PropertyVetoException.class)) {
        pd.setConstrained(true);
    }
}

其中if判断了返回值为void的类型才会有效

问题原因定位到了,那么怎么解决呢?有以下几种方法:
1、升级EasyExcel版本号(未验证过,记得以前其他项目好像没这个问题,应该新版本解决了)

2、导入的对象上不使用@Data注解,手写get和set方法

3、导入的对象上继续使用@Data注解,但是需要增加一个@Accessors(chain = false),指定生成的set方法不使用链式结构,也就是返回值为void

参考资料:
https://blog.csdn.net/weixin_42882845/article/details/119517758

相关版本号:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.6</version>
    <scope>provided</scope>
</dependency>

附一个测试代码:

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("field1", "name");
        map.put("field2", "name");

        Model model = new Model();
        BeanMap.create(model).putAll(map);

        System.out.println(model.getField1());
        System.out.println(model.getField2());
    }

}

class Model{

    private String field1;

    private String field2;

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public Model setField2(String field2) {
        this.field2 = field2;
        return this;
    }

    public String getField1() {
        return field1;
    }

    public String getField2() {
        return field2;
    }

}

觉得内容还不错?打赏个钢镚鼓励鼓励!!👍

闪魔(SmartDevil) 鼠标垫 大号电脑桌垫办公学生学习书桌垫纯色创意键盘垫 【细腻质感】800*400mm丨黑色 防水防污防
¥27.00
金印典tn1035粉盒dcp-1608硒鼓兄弟打印机墨盒DCP-1519 HL-1208/1218W 2000页】标准版粉盒/1支装
¥27.00
名流 避孕套 安全套套 超薄润滑大颗粒螺纹 男用 成人计生 72只家庭量贩装+赠名流加厚避孕套2只装
¥39.00
品胜(PISEN) 苹果数据线PD20W快充线手机充电线器适用iPhone14\/13promax PD 20w快充不伤机|弹窗包赔【1.8米加长】
¥29.00
小学生同步作文·五年级(合订本)(2022年新 人教版小学语文课外阅读理解专项训练)
¥26.52
飞雕开关插座面板10a五孔墙壁插座86型单开暗装电源5孔网线网络16A空调带开关面板雕琢白 三开单控
¥25.20
张生生 辣白菜腌料辣白菜专用酱料腌制韩国辣白菜酱袋装韩式朝鲜族泡菜辣椒酱拌料调味料 辣白菜腌料400g*1袋
¥8.60
云上山溪 山茱萸180g 生山萸肉果干囊枣皮山芋肉去核泡水泡酒养生茶
¥42.90