在面向对象编程中必不可少需要在代码中定义对象模型,而在基于Java的业务平台开发实践中尤其如此。相信大家在平时开发中也深有感触,本来是没有多少代码开发量的,但是因为定义的业务模型对象比较多,而需要重复写Getter/Setter、构造器方法、字符串输出的ToString方法和Equals/HashCode方法等。那么是否一款插件或工具能够替大家完成这些繁琐的操作呢?本文将向大家介绍一款在Eclipse/Intellij IDEA主流的开发环境中都可以使用的Java开发神器,同时简要地介绍下其背后自定义注解的原理。

Lombok的简介

Lombok是一款Java开发插件,使得Java开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的Java模型对象(POJO)。在开发环境中使用Lombok插件后,Java开发人员可以节省出重复构建,诸如hashCode和equals这样的方法以及各种业务对象模型的accessor和ToString等方法的大量时间。对于这些方法,它能够在编译源代码期间自动帮我们生成这些方法,并没有如反射那样降低程序的性能。

在Intellij中安装Lombok的插件

想要体验一把Lombok的话,得先在自己的开发环境中安装上对应的插件。下面先为大家展示下如何在Intellij中安装上Lombok插件。

通过IntelliJ的插件中心寻找Lombok

从Intellij插件中心安装Lombok

另外需要注意的是,在使用lombok注解的时候记得要导入lombok.jar包到工程,如果使用的是Maven的工程项目的话,要在其pom.xml中添加依赖如下:

 <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>

Lombok注解使用方法好了,就这么几步后就可以在Java工程中开始用Lombok这款开发神器了。下文将会给大家介绍Lombok中一些注解的使用方法,让大家对如何用这些注解有一个大致的了解。

Lombok常用注解介绍

下面先来看下Lombok中主要几个常用注解介绍:

Lombok的基本使用示例

(1)Val可以将变量申明是final类型。

 public   static void main(String[] args) {

     val setVar = new HashSet<String>();
val listsVar = new ArrayList<String>();
val mapVar = new HashMap<String, String>(); //=>上面代码相当于如下:
final Set<String> setVar2 = new HashSet<>();
final List<String> listsVar2 = new ArrayList<>();
final Map<String, String> maps2 = new HashMap<>(); }

(2)@NonNull注解能够为方法或构造函数的参数提供非空检查。

 public void notNullExample(@NonNull String string) {
//方法内的代码
} //=>上面代码相当于如下: public void notNullExample(String string) {
if (string != null) {
//方法内的代码相当于如下:
} else {
throw new NullPointerException("null");
}
}

(3)@Cleanup注解能够自动释放资源。

 public   void jedisExample(String[] args) {
try {
@Cleanup Jedis jedis = redisService.getJedis();
} catch (Exception ex) {
logger.error(“Jedis异常:”,ex)
} //=>上面代码相当于如下:
Jedis jedis= null;
try {
jedis = redisService.getJedis();
} catch (Exception e) {
logger.error(“Jedis异常:”,ex)
} finally {
if (jedis != null) {
try {
jedis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

(4)@Getter/@Setter注解可以针对类的属性字段自动生成Get/Set方法。

 public class OrderCreateDemoReq{

     @Getter
@Setter
private String customerId; @Setter
@Getter
private String poolId; //其他代码……
} //上面请求Req类的代码相当于如下: public class OrderCreateDemoReq{
private String customerId;
private String poolId; public String getCustomerId(){
return customerId;
} public String getPoolId(){
return poolId;
} public void setCustomerId(String customerId){
this.customerId = customerId;
} public void setPoolId(String poolId){
this.pool = pool;
} }

(5)@ToString注解,为使用该注解的类生成一个toString方法,默认的toString格式为:ClassName(fieldName= fieleValue ,fieldName1=fieleValue)。

@ToString(callSuper=true,exclude="someExcludedField")
public class Demo extends Bar { private boolean someBoolean = true;
private String someStringField;
private float someExcludedField; } //上面代码相当于如下: public class Demo extends Bar { private boolean someBoolean = true;
private String someStringField;
private float someExcludedField; @ Override
public String toString() {
return "Foo(super=" + super.toString() +
", someBoolean=" + someBoolean +
", someStringField=" + someStringField + ")";
}
}

(6)@EqualsAndHashCode注解,为使用该注解的类自动生成equals和hashCode方法。

 @EqualsAndHashCode(exclude = {"id"}, callSuper =true)
public class LombokDemo extends Demo{
private int id;
private String name;
private String gender;
} //上面代码相当于如下: public class LombokDemo extends Demo{ private int id;
private String name;
private String gender; @Override
public boolean equals(final Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
if (!super.equals(o)) return false;
final LombokDemo other = (LombokDemo)o;
if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
return true;
} @Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + super.hashCode();
result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
return result;
} }

(7) @NoArgsConstructor@RequiredArgsConstructor@AllArgsConstructor,这几个注解分别为类自动生成了无参构造器、指定参数的构造器和包含所有参数的构造器。

 @RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> { private int x, y;
@NonNull private T description; @NoArgsConstructor
public static class NoArgsExample {
@NonNull private String field;
} } //上面代码相当于如下:
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> { private int x, y;
@NonNull private T description; @NoArgsConstructor
public static class NoArgsExample {
@NonNull private String field;
} } public class ConstructorExample<T> {
private int x, y;
@NonNull private T description; private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
} public static <T> ConstructorExample<T> of(T description) {
return new ConstructorExample<T>(description);
} @java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
} public static class NoArgsExample {
@NonNull private String field; public NoArgsExample() {
}
}
}

(8)@Data注解作用比较全,其包含注解的集合@ToString@EqualsAndHashCode,所有字段的@Getter和所有非final字段的@Setter@RequiredArgsConstructor。其示例代码可以参考上面几个注解的组合。

(9)@Builder注解提供了一种比较推崇的构建值对象的方式。

 @Builder
public class BuilderExample { private String name;
private int age; @Singular private Set<String> occupations; } //上面代码相当于如下: public class BuilderExample { private String name;
private int age;
private Set<String> occupations; BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
} public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
} public static class BuilderExampleBuilder { private String name;
private int age;
private java.util.ArrayList<String> occupations; BuilderExampleBuilder() {
} public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
} public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
} public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
} public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
} public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
} public BuilderExample build() {
Set<String> occupations = new HashSet<>();
return new BuilderExample(name, age, occupations);
} @verride
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}

(10)@Synchronized注解类似Java中的Synchronized 关键字,但是可以隐藏同步锁。

 public class SynchronizedExample { 

  private final Object readLock = new   Object(); 

  @Synchronized
public static void hello() {
System.out.println("world");
} @Synchronized("readLock")
public void foo() {
System.out.println("bar");
} //上面代码相当于如下: public class SynchronizedExample { private static final Object $LOCK = new Object[0];
private final Object readLock = new Object(); public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
} public void foo() {
synchronized(readLock) {
System.out.println("bar");
}
} }

Lombok背后的自定义注解原理

本文在前三章节主要介绍了Lombok这款Java开发利器中各种定义注解的使用方法,但作为一个Java开发者来说光了解插件或者技术框架的用法只是做到了“知其然而不知其所以然”,如果真正掌握其背后的技术原理,看明白源码设计理念才能真正做到“知其然知其所以然”。好了,话不多说下面进入本章节的正题,看下Lombok背后注解的深入原理。

可能熟悉Java自定义注解的同学已经猜到,Lombok这款插件正是依靠可插件化的Java自定义注解处理API(JSR 269: Pluggable Annotation Processing API)来实现在Javac编译阶段利用“Annotation Processor”对自定义的注解进行预处理后生成真正在JVM上面执行的“Class文件”。有兴趣的同学反编译带有Lombok注解的类文件也就一目了然了。其大致执行原理图如下:

从上面的这个原理图上可以看出Annotation Processing是编译器在解析Java源代码和生成Class文件之间的一个步骤。其中Lombok插件具体的执行流程如下:

从上面的Lombok执行的流程图中可以看出,在Javac 解析成AST抽象语法树之后, Lombok 根据自己编写的注解处理器,动态地修改 AST,增加新的节点(即Lombok自定义注解所需要生成的代码),最终通过分析生成JVM可执行的字节码Class文件。使用Annotation Processing自定义注解是在编译阶段进行修改,而JDK的反射技术是在运行时动态修改,两者相比,反射虽然更加灵活一些但是带来的性能损耗更加大。

需要更加深入理解Lombok插件的细节,自己查阅其源代码是必比可少的。对开源框架代码比较有执着追求的童鞋可以将Lombok的源代码工程从github上download到本地进行阅读和自己调试。下图为Lombok工程源代码的截图:

从熟悉JSR 269: Pluggable Annotation Processing API的同学可以从工程类结构图中发现AnnotationProcessor这个类是Lombok自定义注解处理的入口。该类有两个比较重要的方法一个是init方法,另外一个是process方法。在init方法中,先用来做参数的初始化,将AnnotationProcessor类中定义的内部类(JavacDescriptor、EcjDescriptor)先注册到ProcessorDescriptor类型定义的列表中。其中,内部静态类—JavacDescriptor在其加载的时候就将lombok.javac.apt.LombokProcessor这个类进行对象实例化并注册。

LombokProcessor处理器中,其中的process方法会根据优先级来分别运行相应的handler处理类。Lombok中的多个自定义注解都分别有对应的handler处理类,如下图所示:

可以看出,在Lombok中对于其自定义注解进行实际的替换、修改和处理的正是这些handler类。对于其实现的细节可以具体参考其中的代码。

本文先从Lombok使用角度出发,先介绍了如何在当前主流的Java开发环境—Intellij中安装这块Java插件,随后分别介绍了Lombok中几种主要的常用注解(比如,@Data@CleanUp@ToString@Getter/Setter等),最后将Lombok背后Java自定义注解的原理与源代码结合起来,介绍了Lombok插件背后具体的执行处理流程。限于篇幅,未能对“自定义Java注解处理器”的具体实践和原理进行阐述,后面将另起专题篇幅进行介绍。

@ToString(callSuper=true,exclude="someExcludedField")
public class Demo extends Bar { private boolean someBoolean = true;
private String someStringField;
private float someExcludedField; } //上面代码相当于如下: public class Demo extends Bar { private boolean someBoolean = true;
private String someStringField;
private float someExcludedField; @ Override
public String toString() {
return "Foo(super=" + super.toString() +
", someBoolean=" + someBoolean +
", someStringField=" + someStringField + ")";
}
}

Java开发神器Lombok的使用与原理的更多相关文章

  1. [Java 开发利器Lombok] 常用注解演示

    在以往的对象模型编码时,我们需要写一大堆的get/set以及不同的构造函数等.Lombok为我们提供了一个非常好的插件形式. 在大多数的项目中,只需要使用到以下集中Annotation就足够了,如果需 ...

  2. Java开发神器——MyEclipse CI 2019.4.0 全新发布(附下载)

    MyEclipse线上特惠,在线立享专属折扣!火热开启中>> MyEclipse 2019的升级版本中,推出对Java 11的支持.性能改进及新的连接器等. [MyEclipse CI 2 ...

  3. Java开发利器--Lombok,IDEA端安装教程

    1.插件安装File-Setting-Plugins 2.开启注解支持: 3.安装lombok maven插件 <plugin> <groupId>org.projectlom ...

  4. Lombok的使用与原理

        在面向对象编程中必不可少需要在代码中定义对象模型,而在基于Java的业务平台开发实践中尤其如此.相信大家在平时开发中也深有感触,本来是没有多少代码开发量的,但是因为定义的业务模型对象比较多,而 ...

  5. 提升Java开发效率必看!教你如何在MyEclipse中使用内联搜索

    MyEclipse CI 2019.4.0安装包下载 Inline Search取代传统Eclipse查找和替换对话框,提供更搞笑.更具侵略性的搜索体验——一种不会中断开发工作流程的工具. DevSt ...

  6. MyEclipse导航代码第二弹,Java开发更便捷

    [MyEclipse CI 2019.4.0安装包下载] 无论是在文件之间导航还是在文件中导航,都可以使用大量导航工具来加快工作流程.目前这些导航工具可在MyEclipse,CodeMix中使用. 单 ...

  7. 都2020年了,这5个java IDE神器你还不知道?

    TIOBE的4月份编程语言排行榜出来了,java还是稳坐第一位,java最新的版本也到了13,一直以来java凭借其企业级应用的优势和大量的框架级应用俘获了大量的粉丝和企业客户. 谈到开发者,java ...

  8. Java效率工具Lombok使用与原理

    Java效率工具Lombok使用与原理 我个人觉得 Lombok是一个优化Java代码以及提升开发效率不错的工具.Lombok 的Github地址为:https://github.com/rzwits ...

  9. Java代码简化神器-Lombok

    一.背景 前段时间在开源社区中发现了一个比较牛逼的简化Java代码的神器-Lombok,接着自己写了demo进行测试和练习,感觉真的很不错,特此分享给需要的小伙伴们~ 二.开发之前的准备 1.lomb ...

随机推荐

  1. 基于Aspectj表达式配置的Spring AOP

    AOP(Aspect-Oriented Programming, 面向切面编程):是一种新的方法论, 是对传统OOP(Object-Oriented Programming, 面向对象编程)的补充. ...

  2. TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q

    TCP协议下的服务端并发,GIL全局解释器锁,死锁,信号量,event事件,线程q 一.TCP协议下的服务端并发 ''' 将不同的功能尽量拆分成不同的函数,拆分出来的功能可以被多个地方使用 TCP服务 ...

  3. 如何利用wx.request进行post请求

    1,method 是  get  方式的时候,会将数据转换成 query string method 为 post 时,header为{"Content-Type": " ...

  4. 大端(bigend)与小端(littleend)

                                      大端:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中: 小端:是指数据的高位保存在内存的高地址中,而数据的高 ...

  5. buuctf——facebook1

    分为注册和登陆两个页面 据大佬wp登陆页面又盲注,但我没测试 直接注册后登陆 注册后发现,会显示输入的url 根据目录扫描,发现robots.txt和flag.php robots.txt有源码备份 ...

  6. 浅谈python中__str__和__repr__的区别

    很多时候我们在创建一个类的时候,在终端打印类或者查看的时候一般都不会得到一个太满意的结果 class T: def __init__(self): self.color="red" ...

  7. logback.xml设置mogodb日志打印控制台

    <logger name="org.springframework.data.mongodb.core" level="DEBUG"/>

  8. 使用python爬取天气预报,[python入门案例]

    # 天气网余姚地区爬虫案例 import requests from lxml import etree class WeatherSpider: def __init__(self): self.u ...

  9. 吴裕雄--天生自然Numpy库学习笔记:NumPy 数学函数

    NumPy 包含大量的各种数学运算的函数,包括三角函数,算术运算的函数,复数处理函数等. NumPy 提供了标准的三角函数:sin().cos().tan(). import numpy as np ...

  10. ASA-有关AAA用户登录的问题

    问题示例:I have created a test user that is set to privilege 15 in the config: When I log in to the ASA ...