Lombok遇到的问题

常用的几个注解:
@Data : 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
@AllArgsConstructor : 注在类上,提供类的全参构造
@NoArgsConstructor : 注在类上,提供类的无参构造
@Setter : 注在属性上,提供 set 方法
@Getter : 注在属性上,提供 get 方法
@EqualsAndHashCode : 注在类上,提供对应的 equals 和 hashCode 方法
@Log4j/@Slf4j : 注在类上,提供对应的 Logger 对象,变量名为 log
@Builde

一、Lombok@Setter、@Getter注解对于第一个字母小写,第二个字母大写的属性生成的get-set方法,与idea,Mybatis,Java官方认可的生成的方法是不相同的

1、解决办法1

其实要解决属性问题可以用jackson的@JsonProperty注解去解决,但是这次的问题是探究为啥会出现上述的情况,

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class TestModel {
    private String abc;
    @JsonProperty("eFg")
    private String eFg;
    @JsonProperty("hIJ")
    private String hIJ;
    @JsonProperty("KLMN")
    private String KLMN;
    private String myModel;
}

2、解决办法2

TestModel 类去掉@Data
手写或idea自动生成属性的getter、setter
注意:KLMN这个全大写的属性比较特殊,用@JsonProperty("KLMN")

根本原因:lombok的@Data和spring的jackson对于getter和setter的生成方式不一样照成的,
Lombok@Setter、@Getter注解对于第一个字母小写,第二个字母大写的属性生成的get-set方法,与idea,Mybatis,Java官方认可的生成的方法是不相同的,主要区别在于第一个字母是小写还是大写。这样就会导致一些序列化问题,比如mybatis框架就不能把此类字段的值序列化到数据库。
lombox 生成第一个是大写

查询编译后的class文件即可
@Data
public class PhoneVo {
    private String iPhone;

   // public String getIPhone() {
     //   return iPhone;
    }//

//    public void setIPhone(String iPhone) {
  //      this.iPhone = iPhone;
    //}
}

spring生产的第一个是小写

public class PhoneVo {
    private String iPhone;

    public String getiPhone() {
        return iPhone;
    }

    public void setiPhone(String iPhone) {
        this.iPhone = iPhone;
    }
}

究竟谁对谁错

lombok官方的解释是这样的

JavaBeans的规范就是这样的,Lombok只是遵循这个规范⽽已,并且不应该使⽤⾸字母⼩写,第⼆个字母⼤写这样的
命名规则,⽽Spring的处理⽅式才是没有遵循JavaBean的规范。除⾮Oracle官⽅推荐如此或者⼤家都是这样处理的
化,Lombok才会进⾏修改

总结

  • 遇到相应的问题,比如属性转换不了,属性名不对,可以使用json属性别名等注解,如jackson用@JsonProperty,fastjson用@JSONField 注:SpringBoot使用的的Jackson
  • 尽量避免非正规或者有歧义的变量书写,如上述的首字母小写第二字母大写、首字母大写等变量名称,明知有歧义的话就要尽量避免,免得有奇怪的bug,命名要符合java驼峰写法

二、@Accessor(chain = true)注解的问题

easyexcel底层使用的是cglib来做反射工具包的:但是cglib使用的是Java的rt.jar里面的一个Introspector这个类的方法:只支持获取返回值不是void的setxxx方法

# Introspector.java 第520if (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);
    }
}

三、使用@Data注解后重写了equals()方法,但是如果只是使用@Data时equals()中只用到了本类的属性,没有考虑继承下来的属性,例如:B类继承了A类

解决
Lombok遇到的问题_第1张图片

四、@Builde

虽然生成的代码有所增加,但少了一个关键的无参构造方法,这可是一个很关键的构造方法,在好的框架里都会调用这个无参构造方法的。如果没有,肯定会报一大堆错误的。
既然刚才那种方式不同,我们可以给刚才那个空构造方法,再加一个注解,这个注解是@Tolerate,有了这个注解,可以让lombok在处理的时候,直接忽略这个构造方法,我们来看下效果。可以看到生成的class文件里面,已经有空构造方法了。
Lombok遇到的问题_第2张图片

五、@Builder变量初始化问题

对于某些字段会有初始值是一个固定值或可以自动生成的场景,比如我们希望每次build一个新的User对象时给id生成一个初始值,直观上说我们希望通过下面的代码来实现:

@Builder
public class User {
    private UUID id = UUID.randomUUID();
    private String name;
}

理想的情况上面代码中的private UUID id = UUID.randomUUID();会起到生成初始值的效果,但其实并没有, 如下是编译后生成的代码, 可以看到生成的UserBuilder类的id变量并没有初始值,而在执行build方法时会new一个User对象,这是如果没有显式的指定id,则build出来的User对象的id就会是null。

public class User {
    private UUID id = UUID.randomUUID();
    private String name;

    User(final UUID id, final String name) {
        this.id = id;
        this.name = name;
    }

    public static User.UserBuilder builder() {
        return new User.UserBuilder();
    }

    public static class UserBuilder {
        private UUID id;
        private String name;

        UserBuilder() {
        }

        public User.UserBuilder id(final UUID id) {
            this.id = id;
            return this;
        }

        public User.UserBuilder name(final String name) {
            this.name = name;
            return this;
        }

        public User build() {
            return new User(this.id, this.name);
        }
    }

在如果按上面的代码来写初始值的赋值,在编译时Lombok会产生一条警告,提示@Builder会忽略此初始值,建议使用@Builder.Default

@Builder will ignore the initializing expression entirely. If you want the initializing expression to serve as default, add @Builder.Default

解决办法: @Builder.Default

@Builder
public class User {
    @Builder.Default
    private UUID id = UUID.randomUUID();
    private String name;
}

总结:

使用@Builder.Default初始化变量
@Builder要注意与其他注解的使用,特别是构造函数和@EqualsAndHashCode
使用@Builder(toBuilder = true) 只能实现浅拷贝
@NonNull,如果检测结果为null则抛出NullPointerException. 反而显式的null-check是更好的选择。

你可能感兴趣的:(java,spring,json)