Java效率工具Lombok使用与原理

我个人觉得 Lombok是一个优化Java代码以及提升开发效率不错的工具。Lombok 的Github地址为:https://github.com/rzwitserloot/lombok ,目前已经有7.9k star。Lombok主要为Java提供了不少语法糖,其中很多的设计都符合《Effective Java》所描述的Java编程最佳实践。Lombok 的学习成本很低,几乎可以直接上手,但是在团队开发的时候要注意,如果团队开发要使用这种工具一定要沟通好,团队其它人不一定装了这个插件。总的来说,Lombok 是每一个 Java 开发人员值得尝试的一个工具。这篇文章是我当初学习 Lombok 觉得非常不错的一篇文章,原文地址:Java效率工具之Lombok

简介

Lombok 是一款好用顺手的工具,就像 Google Guava 一样,在此予以强烈推荐,每一个Java工程师都应该使用它。Lombok 是一种 Java 实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现 Lombok,开发人员可以节省构建诸如hashCode()equals()这样的方法以及以往用来分类各种accessor和mutator的大量时间。

IntelliJ安装Lombok

  1. 第一步:通过IntelliJ的插件中心安装

  2. 第二步:安装插件

  3. 最后需要注意的是,在使用lombok注解的时候记得要导入lombok.jar包到工程,如果使用的是Maven Project,要在pom.xml中添加依赖。

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

Lombok用法

Lombok注解说明

  • val:用在局部变量前面,相当于将变量声明为final
  • @NonNull:给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出NPE(NullPointerException)
  • @Cleanup:自动管理资源,用在局部变量之前,在当前变量范围内即将执行完毕退出之前会自动清理资源,自动生成try-finally这样的代码来关闭流。虽然自JDK7以来,原生引入了try--with--resource结构,但还是不如@Cleanup来的简洁。
  • @Getter/@Setter:用在属性上,再也不用自己手写setter和getter方法了,还可以指定访问范围
  • @ToString:用在类上,可以自动覆写toString方法,当然还可以加其他参数,例如@ToString(exclude=”id”)排除id属性,或者@ToString(callSuper=true, includeFieldNames=true)调用父类的toString方法,包含所有属性
  • @EqualsAndHashCode:用在类上,自动生成equals方法和hashCode方法
  • @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor:用在类上,自动生成无参构造和使用所有参数的构造函数以及把所有@NonNull属性作为参数的构造函数,如果指定staticName = “of”参数,同时还会生成一个返回类对象的静态工厂方法,比使用构造函数方便很多
  • @Data:注解在类上,相当于同时使用了@ToString@EqualsAndHashCode@Getter@Setter@RequiredArgsConstrutor这些注解,对于POJO类十分有用
  • @Value:用在类上,是@Data的不可变形式,相当于为属性添加final声明,只提供getter方法,而不提供setter方法
  • @Builder:用在类、构造器、方法上,为你提供复杂的builder APIs,让你可以像如下方式一样调用Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();更多说明参考Builder
  • @SneakyThrows:自动抛受检异常,而无需显式在方法上使用throws语句
  • @Synchronized:用在方法上,将方法声明为同步的,并自动加锁,而锁对象是一个私有的属性$lock$LOCK,而java中的synchronized关键字锁对象是this,锁在this或者自己的类对象上存在副作用,就是你不能阻止非受控代码去锁this或者类对象,这可能会导致竞争条件或者其它线程错误
  • @Getter(lazy=true):可以替代经典的Double Check Lock样板代码
  • @Log:根据不同的注解生成不同类型的log对象,但是实例名称都是log,有六种可选实现类
  • @CommonsLog Creates log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
  • @Log Creates log = java.util.logging.Logger.getLogger(LogExample.class.getName());
  • @Log4j Creates log = org.apache.log4j.Logger.getLogger(LogExample.class);
  • @Log4j2 Creates log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
  • @Slf4j Creates log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  • @XSlf4j Creates log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);

Lombok代码示例

  1. val 示例

     public static void main(String[] args) {
    val sets = new HashSet<String>();
    val lists = new ArrayList<String>();
    val maps = new HashMap<String, String>();
    //=>相当于如下
    final Set<String> sets2 = new HashSet<>();
    final List<String> lists2 = new ArrayList<>();
    final Map<String, String> maps2 = new HashMap<>();
    }
  2. @NonNull示例

     public void notNullExample(@NonNull String string) {
    string.length();
    }
    //=>相当于
    public void notNullExample(String string) {
    if (string != null) {
    string.length();
    } else {
    throw new NullPointerException("null");
    }
    }
  3. @Cleanup示例

    public static void main(String[] args) {
    try {
    @Cleanup InputStream inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    //=>相当于
    InputStream inputStream = null;
    try {
    inputStream = new FileInputStream(args[0]);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } finally {
    if (inputStream != null) {
    try {
    inputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
  4. @Getter/@Setter示例

     @Setter(AccessLevel.PUBLIC)
    @Getter(AccessLevel.PROTECTED)
    private int id;
    private String shap;
  5. @ToString示例

     @ToString(exclude = "id", callSuper = true, includeFieldNames = true)
    public class LombokDemo {
    private int id;
    private String name;
    private int age;
    public static void main(String[] args) {
    //输出LombokDemo(super=LombokDemo@48524010, name=null, age=0)
    System.out.println(new LombokDemo());
    }
    }
  6. @EqualsAndHashCode示例

     @EqualsAndHashCode(exclude = {"id", "shape"}, callSuper = false)
    public class LombokDemo {
    private int id;
    private String shap;
    }
  7. @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor示例

     @NoArgsConstructor
    @RequiredArgsConstructor(staticName = "of")
    @AllArgsConstructor
    public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    public static void main(String[] args) {
    new LombokDemo(1, "circle");
    //使用静态工厂方法
    LombokDemo.of(2, "circle");
    //无参构造
    new LombokDemo();
    //包含所有参数
    new LombokDemo(1, "circle", 2);
    }
    }
  8. @Data示例

     import lombok.Data;
    @Data
    public class Menu {
    private String shopId;
    private String skuMenuId;
    private String skuName;
    private String normalizeSkuName;
    private String dishMenuId;
    private String dishName;
    private String dishNum;
    //默认阈值
    private float thresHold = 0;
    //新阈值
    private float newThresHold = 0;
    //总得分
    private float totalScore = 0;
    }
  9. @Value示例

     @Value
    public class LombokDemo {
    @NonNull
    private int id;
    @NonNull
    private String shap;
    private int age;
    //相当于
    private final int id;
    public int getId() {
    return this.id;
    }
    ...
    }
  10. @Builder示例

     @Builder
    public class BuilderExample {
    private String name;
    private int age;
    @Singular
    private Set<String> occupations;
    public static void main(String[] args) {
    BuilderExample test = BuilderExample.builder().age(11).name("test").build();
    }
    }
  11. @SneakyThrows示例

     import lombok.SneakyThrows;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    public class Test {
    @SneakyThrows()
    public void read() {
    InputStream inputStream = new FileInputStream("");
    }
    @SneakyThrows
    public void write() {
    throw new UnsupportedEncodingException();
    }
    //相当于
    public void read() throws FileNotFoundException {
    InputStream inputStream = new FileInputStream("");
    }
    public void write() throws UnsupportedEncodingException {
    throw new UnsupportedEncodingException();
    }
    }
  12. @Synchronized示例

     public class SynchronizedDemo {
    @Synchronized
    public static void hello() {
    System.out.println("world");
    }
    //相当于
    private static final Object $LOCK = new Object[0];
    public static void hello() {
    synchronized ($LOCK) {
    System.out.println("world");
    }
    }
    }
  13. @Getter(lazy = true)

     public class GetterLazyExample {
    @Getter(lazy = true)
    private final double[] cached = expensive();
    private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
    result[i] = Math.asin(i);
    }
    return result;
    }
    } // 相当于如下所示:
    import java.util.concurrent.atomic.AtomicReference;
    public class GetterLazyExample {
    private final AtomicReference<java.lang.Object> cached = new AtomicReference<>();
    public double[] getCached() {
    java.lang.Object value = this.cached.get();
    if (value == null) {
    synchronized (this.cached) {
    value = this.cached.get();
    if (value == null) {
    final double[] actualValue = expensive();
    value = actualValue == null ? this.cached : actualValue;
    this.cached.set(value);
    }
    }
    }
    return (double[]) (value == this.cached ? null : value);
    }
    private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
    result[i] = Math.asin(i);
    }
    return result;
    }
    }

Lombok注解原理

说到 Lombok,我们就得去提到 JSR 269: Pluggable Annotation Processing API (www.jcp.org/en/jsr/deta…) 。JSR 269 之前我们也有注解这样的神器,可是我们比如想要做什么必须使用反射,反射的方法局限性较大。首先,它必须定义@Retention为RetentionPolicy.RUNTIME,只能在运行时通过反射来获取注解值,使得运行时代码效率降低。其次,如果想在编译阶段利用注解来进行一些检查,对用户的某些不合理代码给出错误报告,反射的使用方法就无能为力了。而 JSR 269 之后我们可以在 Javac的编译期利用注解做这些事情。所以我们发现核心的区分是在 运行期 还是 编译期

从上图可知,Annotation Processing 是在解析和生成之间的一个步骤。具体详细步骤如下:

上图是 Lombok 处理流程,在Javac 解析成抽象语法树之后(AST), Lombok 根据自己的注解处理器,动态的修改 AST,增加新的节点(所谓代码),最终通过分析和生成字节码

自从Java 6起,javac就支持“JSR 269 Pluggable Annotation Processing API”规范,只要程序实现了该API,就能在javac运行的时候得到调用

  1. 常用的项目管理工具Maven所使用的java编译工具来源于配置的第三方工具,如果我们配置这个第三方工具为Oracle javac的话,那么Maven也就直接支持lombok了;
  2. Intellij Idea配置的编译工具为Oracle javac的话,也就直接支持lombok了;

IDE工具问题解决:

现在有一个A类,其中有一些字段,没有创建它们的setter和getter方法,使用了lombok的@Data注解,另外有一个B类,它调用了A类实例的相应字段的setter和getter方法

编译A类和B类所在的项目,并不会报错,因为最终生成的A类字节码文件中存在相应字段的setter和getter方法

但是,IDE发现B类源代码中所使用的A类实例的setter和getter方法在A类源代码中找不到定义,IDE会认为这是错误

要解决以上这个不是真正错误的错误,可以下载安装Intellij Idea中的"Lombok plugin"。

自定义支持JSR269的注解

一般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
}
}

Java效率工具Lombok使用与原理的更多相关文章

  1. java效率工具 Lombok

    Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toStringm异常处理,I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生. ...

  2. Java 效率工具, 大幅度提高开发效率

    你是否有遇到过这样的情况,在开发过程中需要比较两列数据,但使用文本比对工具的话他是按行基准比对的,我还得对每列数据先进行排序,但排序又去哪里排, 想到 excel 可以排序 , 折腾下来,特别麻烦, ...

  3. Java效率工具之Lombok

    参考: http://www.54tianzhisheng.cn/2018/01/09/lombok/ https://zhuanlan.zhihu.com/p/32779910

  4. 基于IDEA工具 lombok 的使用

    一.简介 Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO).它通过注解实现这一目的. 二.lombok的添加和常用注解 ...

  5. java开发工具比较(16个工具修订版)

    1.JDK (Java Development Kit)Java开发工具集 SUN的Java不仅提了一个丰富的语言和运行环境,而且还提了一个免费的Java开发工具集(JDK).开发人员和最终用户可以利 ...

  6. Java 常用数据结构对象的实现原理 集合类 List Set Map 哪些线程安全 (美团面试题目)

    Java中的集合包括三大类,它们是Set.List和Map, 它们都处于java.util包中,Set.List和Map都是接口,它们有各自的实现类. List.Set都继承自Collection接口 ...

  7. 十四种Java开发工具点评

    在计算机开发语言的历史中,从来没有哪种语言象Java那样受到如此众多厂商的支持,有如此多的开发工具,Java菜鸟们如初入大观园的刘姥姥,看花了眼,不知该何种选择.的确,这些工具各有所长,都没有绝对完美 ...

  8. Java开发工具全面比较

    1.JDK (Java Development Kit)Java开发工具集 从初学者角度来看Java开发工具,采用JDK开发Java程序能够很快理解程序中各部分代码之间的关系,有利于理解Java面向对 ...

  9. 十分钟搞懂Lombok使用与原理

    1 简介 Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它.Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的 ...

随机推荐

  1. C++ 快读快写

    inline int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; c ...

  2. Top 命令数据分析

    一.top 命令详解 当前时间 20:27:12 当前系统运行时间 3:18秒 1个用户 系统负载平均长度为 0.00,0.00,0.00(分别为1分钟.5分钟.15分钟前到现在的平均值) 第二行为进 ...

  3. 15分钟从零开始搭建支持10w+用户的生产环境(四)

    上一篇文章,介绍了这个架构中,WebServer的选择,以及整个架构中扩展时的思路. 原文地址:15分钟从零开始搭建支持10w+用户的生产环境(三)   五.架构实践 前边用了三篇文章,详细介绍了这个 ...

  4. turtle库应用实例2-六芒星的绘制

    六芒星的绘制 ‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪ ...

  5. 推荐一款Python数据可视化神器

    1. 前言 在日常工作中,为了更直观的发现数据中隐藏的规律,察觉到变量之间的互动关系,人们常常借助可视化帮助我们更好的给他人解释现象,做到一图胜千文的说明效果. 在Python中,常见的数据可视化库有 ...

  6. 错误:Several ports (8005, 8080, 8009) required by Tomcat v7.0 Server at localhost are already in use.

    Several ports (8005, 8080, 8009) required by Tomcat v7.0 Server at localhost are already in use. The ...

  7. [hdu5534]DP

    题目原意:给一棵n个点的树添加边,给定度函数f(d)为一个点的度的函数,求所有点的度函数的和 思路: 函数只与点的度有关,而与点无关,n个点的树有n-1条边,共产生2(n-1)个度,每个点至少有1个度 ...

  8. Linux --如何新增一块硬盘并自动挂载

    1. 虚拟机添加硬盘 2.  分区 fdisk /dev/sdb 3. 格式化 mkfs -t ext4 /dev/sdb1 将刚刚创建的盘格式化成 ext4格式 4. 挂载 先创建一个目录,/hom ...

  9. 基于Nettty打造自己的MVC服务器

    最近开始折腾Netty,体验下NIO编程.既然学习了,就要做点东西出来,要不然不容易掌握学到的东西.在Netty的官方demo上都有各种case的sample,打造Http服务器的核心代码就是从Sam ...

  10. C++语言字符串处理函数

    C++语言提供了比C语言更丰富的字符串处理功能.它可以在字符串上经行输入,输出,合并,修改,比较,转换,复制,搜索等操作.使用这些现成的功能可以大大减少我们的编程的负担. 输入和输出的字符串函数,如p ...