常用的几个注解:
@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 第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 setterpd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);if (throwsException(method, PropertyVetoException.class)) {pd.setConstrained(true);}
}
三、使用@Data注解后重写了equals()方法,但是如果只是使用@Data时equals()中只用到了本类的属性,没有考虑继承下来的属性,例如:B类继承了A类
解决
四、@Builde
虽然生成的代码有所增加,但少了一个关键的无参构造方法,这可是一个很关键的构造方法,在好的框架里都会调用这个无参构造方法的。如果没有,肯定会报一大堆错误的。
既然刚才那种方式不同,我们可以给刚才那个空构造方法,再加一个注解,这个注解是@Tolerate,有了这个注解,可以让lombok在处理的时候,直接忽略这个构造方法,我们来看下效果。可以看到生成的class文件里面,已经有空构造方法了。
五、@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.Defaultprivate UUID id = UUID.randomUUID();private String name;
}
总结:
使用@Builder.Default初始化变量
@Builder要注意与其他注解的使用,特别是构造函数和@EqualsAndHashCode
使用@Builder(toBuilder = true) 只能实现浅拷贝
@NonNull,如果检测结果为null则抛出NullPointerException. 反而显式的null-check是更好的选择。