常用的几个注解:
@Data : 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
@AllArgsConstructor : 注在类上,提供类的全参构造
@NoArgsConstructor : 注在类上,提供类的无参构造
@Setter : 注在属性上,提供 set 方法
@Getter : 注在属性上,提供 get 方法
@EqualsAndHashCode : 注在类上,提供对应的 equals 和 hashCode 方法
@Log4j/@Slf4j : 注在类上,提供对应的 Logger 对象,变量名为 log
@Builde
其实要解决属性问题可以用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;
}
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才会进⾏修改
总结
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 setter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
if (throwsException(method, PropertyVetoException.class)) {
pd.setConstrained(true);
}
}
虽然生成的代码有所增加,但少了一个关键的无参构造方法,这可是一个很关键的构造方法,在好的框架里都会调用这个无参构造方法的。如果没有,肯定会报一大堆错误的。
既然刚才那种方式不同,我们可以给刚才那个空构造方法,再加一个注解,这个注解是@Tolerate,有了这个注解,可以让lombok在处理的时候,直接忽略这个构造方法,我们来看下效果。可以看到生成的class文件里面,已经有空构造方法了。
对于某些字段会有初始值是一个固定值或可以自动生成的场景,比如我们希望每次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
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是更好的选择。