Lombok意思是龙目岛。该岛是巴厘岛的一座附属岛屿,虽然风景优美,但并非本文主题。本文要介绍的Lombok一样风景绮丽,它乃是java的一个强大工具,能极大的减少代码量,并使代码更加整洁清晰。
使用前的准备
1.添加依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.4</version>
</dependency>
2.安装插件
为了视觉效果,安装插件是必须的,否则会有很多’飘红’。插件可直接在idea中进行搜索和安装。
3.修改idea配置
由于lombok基于插入式注解引擎,只在编译器生成代码,因此进行编译的时候,需要idea开启注解引擎支持功能,其功能默认开启,但有时候是关闭状态,需要我们自己手动开启,否则idea会编译报错:
基本用法
1.生成Getter/Setter方法
考虑实体对象Community有四个属性,一般情况下我们会使用idea生成工具,生成Getter和Setter方法。如下代码:
public class Community {
private String communityId;
private String name;
private String title;
private int type;
/**
* Getter method for property communityId.
*
* @return property value of communityId
*/
public String getCommunityId() {
return communityId;
}
/**
* Setter method for property communityId.
*
* @param communityId value to be assigned to property communityId
*/
public void setCommunityId(String communityId) {
this.communityId = communityId;
}
/**
* Getter method for property name.
*
* @return property value of name
*/
public String getName() {
return name;
}
/**
* Setter method for property name.
*
* @param name value to be assigned to property name
*/
public void setName(String name) {
this.name = name;
}
/**
* Getter method for property title.
*
* @return property value of title
*/
public String getTitle() {
return title;
}
/**
* Setter method for property title.
*
* @param title value to be assigned to property title
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Getter method for property type.
*
* @return property value of type
*/
public int getType() {
return type;
}
/**
* Setter method for property type.
*
* @param type value to be assigned to property type
*/
public void setType(int type) {
this.type = type;
}
}
这代码很长,如果是一个单独的类还好,如果是内部类,那么会使得该类相当难读,使人分不清主次。采用lombok则可以直接:
public class Community {
@Getter @Setter
private String communityId;
@Getter @Setter
private String name;
@Getter @Setter
private String title;
@Getter @Setter
private int type;
}
@Getter和@Setter都可以单独定义函数的访问等
public class Community {
@Getter (AccessLevel.PUBLIC)
@Setter(AccessLevel.PRIVATE)
private String communityId;
@Getter(AccessLevel.PUBLIC)
@Setter(AccessLevel.PUBLIC)
private String name;
@Getter(AccessLevel.PUBLIC)
@Setter(AccessLevel.PUBLIC)
private String title;
@Getter(AccessLevel.PUBLIC)
@Setter(AccessLevel.PUBLIC)
private int type;
}
如果都是public,则代码可以更加简洁:
@Getter @Setter
class Community {
private String communityId;
private String name;
private String title;
private int type;
}
2.生成构造方法
如果我们要给Community实体类生成构造方法,那么可以如下代码示例:
public class Community {
private String communityId;
private String name;
private String title;
private int type;
public Community(String communityId, String name, String title, int type) {
this.communityId = communityId;
this.name = name;
this.title = title;
this.type = type;
}
public Community() {
}
public Community(String communityId, String name) {
this.communityId = communityId;
this.name = name;
}
}
可以看到很麻烦,也很混乱,如果使用lombok则变得清晰简洁:
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor(staticName="of")
class Community {
private String communityId;
@NonNull private String name;
@NonNull private String title;
private int type;
}
其中@AllArgsConstructor用来指定全参数构造器,@NoArgsConstructor用来指定无参数构造器,@RequiredArgsConstructor用来指定参数(采用静态方法of访问)
3.生成equals、hashcode、toString
我们在来看,如果需要生成equals、hashcode、toString呢?按照平常的做法:
public class Community {
private String communityId;
private String name;
private String title;
private int type;
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
Community community = (Community)o;
if (type != community.type) { return false; }
if (communityId != null ? !communityId.equals(community.communityId) : community.communityId != null) {
return false;
}
if (name != null ? !name.equals(community.name) : community.name != null) { return false; }
return title != null ? title.equals(community.title) : community.title == null;
}
@Override
public int hashCode() {
int result = communityId != null ? communityId.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + type;
return result;
}
@Override
public String toString() {
return "Community{" +
"communityId=‘" + communityId + ‘\\‘‘ +
", name=‘" + name + ‘\\‘‘ +
", title=‘" + title + ‘\\‘‘ +
", type=" + type +
‘}‘;
}
}
可以看到,代码相当的恼人,如果使用lombok:
@ToString
@EqualsAndHashCode
class Community {
private String communityId;
private String name;
private String title;
private int type;
}
如果我们只需要是否name作为equals和hashcode的运算字段,并且不想将title toString出来:
@ToString(exclude = {"title"})
@EqualsAndHashCode(of = {"name"})
class Community {
private String communityId;
private String name;
private String title;
private int type;
}
结合上面所有的实例,我们可以使用如下代码就能完成平常需要上百行代码才能完成的事情:
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor(staticName="of")
@Getter @Setter
@ToString(exclude = {"title"})
@EqualsAndHashCode(of = {"name"})
public class Community {
private String communityId;
@NonNull private String name;
@NonNull private String title;
private int type;
}
我们可以看到,上节中类上面打了好多注解,还是显得有些乱,如果我们没有要求那么多定制化需求,则可以直接使用@Data注解,他包含了:@Getter @Setter @ToString @EqualsAndHashCode RequiredArgsConstructor注解,因此可以简化为:
@NoArgsConstructor
@AllArgsConstructor
@Data(staticConstructor="of")
public class Community {
private String communityId;
@NonNull private String name;
@NonNull private String title;
private int type;
}
是不是极其简洁,表达力又极强呢?
5.Builder模式
有的时候,我们喜欢采用Builder模式去构造一个对象,比如如下代码:
class Community {
private String communityId;
private String name;
private String title;
private int type;
@java.beans.ConstructorProperties({"communityId", "name", "title", "type"})
Community(String communityId, String name, String title, int type) {
this.communityId = communityId;
this.name = name;
this.title = title;
this.type = type;
}
public static CommunityBuilder builder() {return new CommunityBuilder();}
public static class CommunityBuilder {
private String communityId;
private String name;
private String title;
private int type;
CommunityBuilder() {}
public Community.CommunityBuilder communityId(String communityId) {
this.communityId = communityId;
return this;
}
public Community.CommunityBuilder name(String name) {
this.name = name;
return this;
}
public Community.CommunityBuilder title(String title) {
this.title = title;
return this;
}
public Community.CommunityBuilder type(int type) {
this.type = type;
return this;
}
public Community build() {
return new Community(communityId, name, title, type);
}
public String toString() {
return "com.qunar.kris.share.misc.lombok.Community.CommunityBuilder(communityId=" + this.communityId
+ ", name="
+ this.name + ", title=" + this.title + ", type=" + this.type + ")";
}
}
}
然后我们可以这么使用:
public static void main(String[] args) {
Community community = Community.builder()
.communityId("zzz")
.name("xxx")
.title("yyy")
.type(1).build();
}
上述创建builder的方式是在是太麻烦了,这只是4个参数,如果更多的参数,可想而知代码量啊!使用Lombok则很简单:
@Builder
class Community {
private String communityId;
private String name;
private String title;
private int type;
}
以上均是在日常项目中非常常用的特性,我们再来看一些使用频率较低的特写,可作为读者的参考。
基本实现原理
lombok魔法并不神秘,他采用JSR269所提出的插入式注解处理
(Pluggable Annotation Processing),并结合动态代码生成技术所开发的。如下图所示:
上图展示了一个一般javac的编译过程,java文件首先通过进行解析构建出一个AST
,然后执行注解处理,最后经过分析优化生成二进制的.class
文件。我们能做到的是,在注解处理阶段进行一些相应处理。首先我们在META-INF.services下创建如下文件:
文件中指定我们的注解处理器:com.alipay.kris.other.lombok.MyAnnotaionProcessor
然后我们接可以编写自己的注解处理器,一个简单的实例代码如下:
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.alipay.kris.other.lombok.*")
public class MyAnnotaionProcessor extends AbstractProcessor {
public MyAnnotaionProcessor() {
super();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv) {
for (Element elem : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
MyAnnotation annotation = elem.getAnnotation(MyAnnotation.class);
String message = "annotation found in " + elem.getSimpleName()
+ " with " + annotation.value();
addToString(elem);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message);
}
return true; // no further processing of this annotation type
}
}
我们能做到的也就是这么多,但lombok在此基础之上,对AST进行修改,将Setter/Getter等上文提到过的方法’挂载’到AST中。更多的请点击下文的参考资料(blogspot需要FQ)。