Home  >  Article  >  Java  >  Get to know the pitfalls of Lombok

Get to know the pitfalls of Lombok

coldplay.xixi
coldplay.xixiforward
2020-10-09 17:00:022649browse

java Basic Tutorial column introduces you to the pitfalls of Lombok, which is easy to use.

Get to know the pitfalls of Lombok

Preface

The Lombok plug-in was introduced into the project last year, which really freed up your hands and replaced some repetitive simple tasks (Getter, Setter, toString, etc. Method writing), however, during the use process, some pitfalls were also discovered. At first, I did not realize that it was a Lombok problem. Later, I tracked the source code of the corresponding other components and found out that it was a Lombok problem!

The pitfalls of the Setter-Getter method

Problem discovery

We mainly use the annotations of Lombok's Setter-Getter method in the project, which is the combined annotation @Data, but in the project During a process of inserting data using Mybatis, a problem occurred. The problem is described as follows:

我们有个实体类:
@Data
public class NMetaVerify{
    private NMetaType nMetaType;
    private Long id;
    ....其他属性
}复制代码

When we used Mybatis to insert data, we found that other attributes could be inserted normally, but the nMetaType attribute was in the database. Has always been null.

Solution

When I debug the project code to call the method corresponding to inserting SQL in Mybatis, I see that the nMetaType attribute of the NMetaVerify object still has data, but after executing the insertion, the database The nMetaType field is always null. I originally thought that my enumeration type was written incorrectly. After looking at other fields that also have enumeration types, they can be inserted into the database normally, which makes me even more confused. . So, I traced the source code of Mybatis and found that Mybatis used reflection to obtain the nMetaType attribute, using the getxxxx method to obtain it. However, I found that the get method of nMetaType seemed to be a bit different from the getxxxx method required by Mybatis. Same. Problem found!

Reason

The get-set method generated by Lombok for attributes with the first letter in lowercase and the second letter in uppercase is generated by Mybatis and idea or the get-set method officially recognized by Java. The difference:

#Lombok生成的Get-Set方法
@Data
public class NMetaVerify {
    private Long id;
    private NMetaType nMetaType;
    private Date createTime;
    
    public void lombokFound(){
        NMetaVerify nMetaVerify = new NMetaVerify();
        nMetaVerify.setNMetaType(NMetaType.TWO); //注意:nMetaType的set方法为setNMetaType,第一个n字母大写了,
        nMetaVerify.getNMetaType();                                  //getxxxx方法也是大写
    }
}复制代码
#idea,Mybatis,Java官方默认的行为为:
public class NMetaVerify {
    private Long id;
    private NMetaType nMetaType;
    private Date createTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public NMetaType getnMetaType() {//注意:nMetaType属性的第一个字母小写
        return nMetaType;
    }

    public void setnMetaType(NMetaType nMetaType) {//注意:nMetaType属性的第一个字母小写
        this.nMetaType = nMetaType;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}复制代码

Mybatis (version 3.4.6) parses the get-set method to obtain the source code of the attribute name:

package org.apache.ibatis.reflection.property;

import java.util.Locale;

import org.apache.ibatis.reflection.ReflectionException;

/**
 * @author Clinton Begin
 */
public final class PropertyNamer {

        private PropertyNamer() {
            // Prevent Instantiation of Static Class
        }

        public static String methodToProperty(String name) {
            if (name.startsWith("is")) {//is开头的一般是bool类型,直接从第二个(索引)开始截取(简单粗暴)
                    name = name.substring(2);
            } else if (name.startsWith("get") || name.startsWith("set")) {//set-get的就从第三个(索引)开始截取
                    name = name.substring(3);
            } else {
                    throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
            }
            //下面这个判断很重要,可以分成两句话开始解释,解释如下
            //第一句话:name.length()==1
            //                      对于属性只有一个字母的,例如private int x;
            //                      对应的get-set方法是getX();setX(int x);
            //第二句话:name.length() > 1 && !Character.isUpperCase(name.charAt(1)))
            //                      属性名字长度大于1,并且第二个(代码中的charAt(1),这个1是数组下标)字母是小写的
            //                      如果第二个char是大写的,那就直接返回name
            if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
                    name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);//让属性名第一个字母小写,然后加上后面的内容
            }

            return name;
        }

        public static boolean isProperty(String name) {
                return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
        }

        public static boolean isGetter(String name) {
                return name.startsWith("get") || name.startsWith("is");
        }

        public static boolean isSetter(String name) {
                return name.startsWith("set");
        }

}复制代码

Mybatis parses the get-set method to test the attribute name

    @Test
    public void foundPropertyNamer() {
        String isName = "isName";
        String getName = "getName";
        String getnMetaType = "getnMetaType";
        String getNMetaType = "getNMetaType";

        Stream.of(isName,getName,getnMetaType,getNMetaType)
                .forEach(methodName->System.out.println("方法名字是:"+methodName+" 属性名字:"+ PropertyNamer.methodToProperty(methodName)));
    }
    
    #输出结果如下:
    方法名字是:isName 属性名字:name 
    方法名字是:getName 属性名字:name 
    方法名字是:getnMetaType 属性名字:nMetaType //这个以及下面的属性第二个字母都是大写,所以直接返回name
    方法名字是:getNMetaType 属性名字:NMetaType复制代码

Solution

1.修改属性名字,让第二个字母小写,或者说是规定所有的属性的前两个字母必须小写
2.如果数据库已经设计好,并且前后端接口对接好了,不想修改,那就专门为这种特殊的属性使用idea生成get-set方法复制代码

@Accessor(chain = true) annotation problem

Problem discovery

Export using easyexcel(github.com/alibaba/eas…) When I checked, I found that the previous entity class exports were normal, but now the newly added entity class is not normal. After comparison, it was found that the newly added entity class added the @Accessor(chain = true) annotation. Our main purpose is It is convenient for us to call the set method in a chain:

new UserDto()
.setUserName("")
.setAge(10)
........
.setBirthday(new Date());复制代码

Reason

The bottom layer of easyexcel uses cglib as the reflection toolkit:

com.alibaba.excel.read.listener.ModelBuildEventListener 类的第130行
BeanMap.create(resultModel).putAll(map);

最底层的是cglib的BeanMap的这个方法调用

abstract public Object put(Object bean, Object key, Object value);复制代码

But cglib uses Java's rt A method of the Introspector class in .jar:

# Introspector.java 第520行
if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
   pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
   //下面这行判断,只获取返回值是void类型的setxxxx方法
 } 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);
    }
}复制代码

Solution

1.去掉Accessor注解
2.要么就等待easyexcel的作者替换掉底层的cglib或者是其他,反正是支持获取返回值不是void的setxxx方法就行复制代码

Related free learning recommendations: java basic tutorial

The above is the detailed content of Get to know the pitfalls of Lombok. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete