Lombok 的正确使用姿势

news2024/11/26 4:43:42

文章目录

    • 1.Lombok 是什么
    • 2.安装 Lombok
    • 3.Spring Boot 集成 Lombok
    • 4.使用 Lombok
      • 4.1 注解一览表
      • 4.2 部分使用介绍
        • @Getter(lazy=true)
        • @Value
        • @Builder
        • @SuperBuilder
        • @SneakyThrows
        • @Slf4j
        • @Cleanup
        • @NonNull
        • @With
        • @Synchronized
        • val

在 Java 开发领域中,Lombok 插件已经成为一个非常流行的代码库。该插件让 Java 开发更加便捷、高效,因此提高了开发者的生产力。

1.Lombok 是什么

Lombok 是一款 Java 开发工具,它可以通过注解来帮助程序员在编译时自动生成 Java 代码,从而简化 Java 开发过程。具体来说他会通过解析注解,然后在编译时生成代码来实现 Java 代码的功能增强。

Lombok 插件产生的主要原因是 Java 语言臃肿的语法,需要大量的样板代码,以及冗长臃肿的 getter 和 setter 方法。当你的模型层非常大时,手动编写所有这些代码会变得非常繁琐和无聊。因此,Lombok 插件为我们自动生成 Java 代码并帮助优化 Java 开发过程,提高效率。

2.安装 Lombok

打开 IDEA 设置页面:

在插件页面搜索“Lombok”安装即可:

注意:使用 Lombok 必须要在 IDE 中安装 Lombok 插件,以使得 IDE 能正确识别和使用 Lombok 注解。

3.Spring Boot 集成 Lombok

为了使用 Lombok 插件,我们需要在项目中设置依赖。下面是一个使用 Maven 添加 Lombok 的 pom.xml 文件的例子:

<!--Lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

注意:SpringBoot 2.1.x 版本后无需指定 Lombok 版本,SpringBoot 在 spring-boot-dependencies 中已经内置了该依赖进行管理。

4.使用 Lombok

4.1 注解一览表

以下是 Lombok 提供的一些主要注解及其功能:

注解功能
@Getter为字段生成 getter 方法
@Setter为字段生成 setter 方法
@Data为字段生成 getter 和 setter 方法,还包括 equals, hashCode, toString 方法
@ToString为类生成 toString 方法
@EqualsAndHashCode为类生成 equals 和 hashCode 方法
@NoArgsConstructor为类生成无参构造器
@AllArgsConstructor为类生成包含所有字段的构造器
@RequiredArgsConstructor为类生成包含必须字段(final 和带有 @NonNull 注解的字段)的构造器
@Value为类生成只读属性(final),包括 getter, equals, hashCode, toString 方法
@Builder为类实现 Builder 模式
@SneakyThrows用于方法上,可以让方法抛出被检查的异常,而不需要显式地在方法上使用 throws 关键字
@Slf4j在类中添加一个名为 ‘log’ 的 SLF4J 日志器对象
@Cleanup自动管理资源,用于自动调用 close 方法释放资源
@NonNull用于检查方法或构造器的参数是否为 null,如果为 null 就会抛出 NullPointerException
@Delegate自动生成委托(delegate)方法,用来将调用转发到指定的字段或方法
@With为字段生成返回更新了某个字段值的新对象的方法
@SuperBuilder用于更复杂的继承情况下的 Builder 模式
@SynchronizedLombok 提供的同步锁,用于方法,将会同步在一个私有的字段上,如果是静态方法,则同步在一个私有的静态字段上
val定义一个局部变量并立即为其赋值,这个变量为 final 类型,不能被修改

4.2 部分使用介绍

上述注释大多数使用看功能描述基本都能理解,下面挑出一些不太容易理解的进行使用说明。

@Getter(lazy=true)

@Getter(lazy=true) 为字段生成延迟初始化的 getter 方法。这个 getter 方法在第一次被调用时初始化字段,并将结果缓存。一般用于需要获取的某一个属性比较消耗资源时,便可以通过该注解添加 lazy=true 属性实现懒加载,这会生成 Double Check Lock 样板代码对属性进行懒加载。

在 Java 中,“double check lock”(双重检查锁)模式是一种常用的多线程并发编程技巧,主要用于延迟初始化并确保只有一个线程能够初始化资源。当你在 Lombok 中使用 @Getter(lazy=true) 注解时,Lombok 就会生成对应的双重检查锁的代码。这样可以确保字段的延迟初始化,在多线程环境中也能保证线程安全,而且只会初始化一次。

import lombok.Getter;

public class LazyGetterExample {

    // 使用双重检查锁对方法进行加锁,确保只有一个线程可以执行 expensive() 方法(懒加载)
    @Getter(lazy = true)
    private final Double cached = expensive();

    private Double expensive() {
        // 模拟一个耗时的操作
        Double result = null;
        for (int i = 0; i < 1000000; i++) {
            result = Math.atan(i) * Math.tan(i);
        }
        return result;
    }

    public static void main(String[] args) {
        LazyGetterExample example = new LazyGetterExample();
        System.out.println(example.getCached());
    }
}

编译后的代码会是如下这样的:

import java.util.concurrent.atomic.AtomicReference;

public class LazyGetterExample {
    private final AtomicReference<Object> cached = new AtomicReference();

    public LazyGetterExample() {
    }

    private Double expensive() {
        Double result = null;

        for(int i = 0; i < 1000000; ++i) {
            result = Math.atan((double)i) * Math.tan((double)i);
        }

        return result;
    }

    public static void main(String[] args) {
        LazyGetterExample example = new LazyGetterExample();
        System.out.println(example.getCached());
    }

    public Double getCached() {
        Object value = this.cached.get();
        if (value == null) {
            synchronized(this.cached) {
                value = this.cached.get();
                if (value == null) {
                    Double actualValue = this.expensive();
                    value = actualValue == null ? this.cached : actualValue;
                    this.cached.set(value);
                }
            }
        }

        return (Double)((Double)(value == this.cached ? null : value));
    }
}

@Value

@Value 为类生成只读属性,即所有字段都是 final,包括 getter 方法、equalshashCodetoString 方法。此时此类相当于 final 类,无法被继承,其属性也会变成 final 属性。

@Value
public class ValueExample {
    private String name;
    private int age;

    public static void main(String[] args) {
        // 只能使用全参构造器
        ValueExample example = new ValueExample("张三", 18);
        // 可以直接获取属性值
        System.out.println(example.getName());
        System.out.println(example.getAge());

        // 不能修改属性值
        // example.setName("李四");
        // example.setAge(20);
    }
}

编译后的代码会是如下这样的:

public final class ValueExample {
    private final String name;
    private final int age;

    public static void main(String[] args) {
        ValueExample example = new ValueExample("张三", 18);
        System.out.println(example.getName());
        System.out.println(example.getAge());
    }

    public ValueExample(final String name, final int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof ValueExample)) {
            return false;
        } else {
            ValueExample other = (ValueExample)o;
            if (this.getAge() != other.getAge()) {
                return false;
            } else {
                Object this$name = this.getName();
                Object other$name = other.getName();
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }

                return true;
            }
        }
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        result = result * 59 + this.getAge();
        Object $name = this.getName();
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        return result;
    }

    public String toString() {
        return "ValueExample(name=" + this.getName() + ", age=" + this.getAge() + ")";
    }
}

@Builder

@Builder 为类实现 Builder 设计模式(建造者模式),通常用于链式构造复杂对象。

import lombok.Builder;
import lombok.ToString;

@ToString
@Builder
public class BuilderExample {
    private String name;
    private int age;

    public static void main(String[] args) {
        BuilderExample example = BuilderExample.builder().name("张三").age(18).build();
        System.out.println(example);
    }
}

编译后的代码会是如下这样的:

public class BuilderExample {
    private String name;
    private int age;

    public static void main(String[] args) {
        BuilderExample example = builder().name("张三").age(18).build();
        System.out.println(example);
    }

    BuilderExample(final String name, final int age) {
        this.name = name;
        this.age = age;
    }

    public static BuilderExampleBuilder builder() {
        return new BuilderExampleBuilder();
    }

    public String toString() {
        return "BuilderExample(name=" + this.name + ", age=" + this.age + ")";
    }

    public static class BuilderExampleBuilder {
        private String name;
        private int age;

        BuilderExampleBuilder() {
        }

        public BuilderExampleBuilder name(final String name) {
            this.name = name;
            return this;
        }

        public BuilderExampleBuilder age(final int age) {
            this.age = age;
            return this;
        }

        public BuilderExample build() {
            return new BuilderExample(this.name, this.age);
        }

        public String toString() {
            return "BuilderExample.BuilderExampleBuilder(name=" + this.name + ", age=" + this.age + ")";
        }
    }
}

@SuperBuilder

@SneakyThrows

@SneakyThrows 用于方法上,可以让方法抛出被检查的异常,而不需要显式地在方法上使用 throws 关键字抛出异常。

import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class SneakyThrowsExample {
    // 自动抛出异常,不需要手动 try catch
    @SneakyThrows(UnsupportedEncodingException.class)
    public String sneakyMethod(){
        return URLEncoder.encode("Example", "Unsupported Encoding");
    }
    
    public static void main(String[] args) {
        SneakyThrowsExample example = new SneakyThrowsExample();
        System.out.println(example.sneakyMethod());
    }
}

编译后的代码会是如下这样的:

public class SneakyThrowsExample {
    public SneakyThrowsExample() {
    }

    public String sneakyMethod() {
        try {
            return URLEncoder.encode("Example", "Unsupported Encoding");
        } catch (UnsupportedEncodingException var2) {
            throw var2;
        }
    }

    public static void main(String[] args) {
        SneakyThrowsExample example = new SneakyThrowsExample();
        System.out.println(example.sneakyMethod());
    }
}

@Slf4j

使用 Lombok 生成日志对象时,根据使用日志实现的不同,有多种注解可以使用。比如 @Log@Log4j@Log4j2@Slf4j 等。以 @Slf4j 为例,它会在类中添加一个名为 ‘log’ 的 SLF4J 日志器对象。

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Slf4jExample {
    public static void main(String[] args) {
        log.info("Hello World");
        log.error("Hello World");
        log.debug("Hello World");
        log.warn("Hello World");
    }
}

编译后的代码会是如下这样的:

public class Slf4jExample {
    private static final Logger log = LoggerFactory.getLogger(Slf4jExample.class);

    public Slf4jExample() {
    }

    public static void main(String[] args) {
        log.info("Hello World");
        log.error("Hello World");
        log.debug("Hello World");
        log.warn("Hello World");
    }
}

@Cleanup

@Cleanup 用于自动管理资源,用于自动调用 close() 方法释放资源,避免了手动释放。

public class CleanupExample {
    public static void main(String[] args) throws IOException {
        @Cleanup InputStream in = Files.newInputStream(Paths.get("file.txt"));
    }
}

编译后的代码会是如下这样的:

public class CleanupExample {
    public CleanupExample() {
    }

    public static void main(String[] args) throws IOException {
        InputStream in = new FileInputStream("/file.txt");
        if (Collections.singletonList(in).get(0) != null) {
            in.close();
        }
    }
}

@NonNull

@NonNull 用于检查方法或构造器的参数是否为 null,如果为 null 就会抛出 NullPointerException,一般用来做非空判断。

public class NonNullExample {
    public String nonNullMethod(@NonNull String string){
        return string;
    }
    
    public static void main(String[] args) {
        NonNullExample example = new NonNullExample();
        example.nonNullMethod(null);
    }
}

编译后的代码会是如下这样的:

public class NonNullExample {
    public NonNullExample() {
    }

    public String nonNullMethod(@NonNull String string) {
        if (string == null) {
            throw new NullPointerException("string is marked non-null but is null");
        } else {
            return string;
        }
    }

    public static void main(String[] args) {
        NonNullExample example = new NonNullExample();
        example.nonNullMethod((String)null);
    }
}

@With

@With 用于为字段生成返回更新了某个字段值的新对象的方法。简单来说,它可以实现对原对象进行克隆,并改变其一个属性,使用时需要指定全参构造方法。

import lombok.AllArgsConstructor;
import lombok.With;

@With
@AllArgsConstructor
public class WithExample {
    private String name;
    private int age;

    public static void main(String[] args) {
        // 实例化一个对象
        WithExample example = new WithExample("javgo", 18);
        System.out.println(example);

        // 使用 withName 方法修改 name 属性
        WithExample withExample = example.withName("javgo2");
        System.out.println(withExample);
    }
}

编译后的代码会是如下这样的:

public class WithExample {
    private String name;
    private int age;

    public static void main(String[] args) {
        WithExample example = new WithExample("javgo", 18);
        System.out.println(example);
        WithExample withExample = example.withName("javgo2");
        System.out.println(withExample);
    }

    public WithExample withName(final String name) {
        return this.name == name ? this : new WithExample(name, this.age);
    }

    public WithExample withAge(final int age) {
        return this.age == age ? this : new WithExample(this.name, age);
    }

    public WithExample(final String name, final int age) {
        this.name = name;
        this.age = age;
    }
}

@Synchronized

当我们在多个线程中访问同一资源时,往往会出现线程安全问题,往往我们会使用 synchronized 关键字修饰方法来实现同步访问。而 @Synchronized 是 Lombok 提供的同步锁,用于方法,将会同步在一个私有的字段上,如果是静态方法,则同步在一个私有的静态字段上。

import lombok.Synchronized;

public class SynchronizedExample {
    private final Object readLock = new Object();

    @Synchronized
    public void syncMethod(){
        // do something
    }

    @Synchronized("readLock")
    public void customLockMethod() {
        // do something
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();

        // 两个方法都会被锁定
        example.syncMethod(); // 锁定的是 this, 即 SynchronizedExample 对象
        example.customLockMethod(); // 锁定的是 readLock,即 SynchronizedExample.readLock 对象
    }
}

编译后的代码会是如下这样的:

public class SynchronizedExample {
    private final Object $lock = new Object[0];
    private final Object readLock = new Object();

    public SynchronizedExample() {
    }

    public void syncMethod() {
        synchronized(this.$lock) {
            ;
        }
    }

    public void customLockMethod() {
        synchronized(this.readLock) {
            ;
        }
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();
        example.syncMethod();
        example.customLockMethod();
    }
}

val

val 用于定义一个任意类型的局部变量并立即为其赋值,这个变量为 final 类型,不能被修改。

import lombok.val;
import java.util.ArrayList;

public class ValExample {
    public static void main(String[] args) {
        val example = new ArrayList<String>();
        example.add("Hello, World!");
    }
}

编译后的代码会是如下这样的:

public class ValExample {
    public ValExample() {
    }

    public static void main(String[] args) {
        ArrayList<String> example = new ArrayList();
        example.add("Hello, World!");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenGL蓝宝书第八章学习笔记:基元处理之几何着色器

前言 本篇在讲什么 OpenGL蓝宝书第八章学习笔记之几何着色器 本篇适合什么 适合初学OpenGL的小白 本篇需要什么 对C语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重实践&#xff0c…

使用scikit-learn和pandas学习线性回归

对于想深入了解线性回归的童鞋&#xff0c;这里给出一个完整的例子&#xff0c;详细学完这个例子&#xff0c;对用scikit-learn来运行线性回归&#xff0c;评估模型不会有什么问题了。 1. 获取数据&#xff0c;定义问题 没有数据&#xff0c;当然没法研究机器学习啦。这里我们用…

buuctf re入门题目解析

目录 1.easyre 2.reverse1 3.reverse2 4.内涵的软件 1.easyre 将exe文件放入ida&#xff0c;在主函数main中找到flag&#xff0c;此题结束 2.reverse1 打开主函数main&#xff0c;发现有一个跳转函数&#xff0c;双击打开 这句命令是将str1和str2的内容比较&#xff0c;当…

「C/C++」C/C++空指针void*

✨博客主页&#xff1a;何曾参静谧的博客 &#x1f4cc;文章专栏&#xff1a;「C/C」C/C程序设计 相关术语 void指针&#xff1a;是一种通用指针类型&#xff0c;可以指向任何类型的数据或对象。它不关心指向的数据或对象的类型&#xff0c;只关心指针本身的地址。因此&#xf…

性能测试—— 基础概念

目录 一、性能测试和功能测试的区别 二、性能测试衡量指标以及名称解释 1、并发用户数、系统用户数、在线用户数 2、响应时间、平均响应时间、请求响应时间 3、事务 4、点击率 5、吞吐量 6、思考时间 7、资源利用率 三、性能测试分类 1、一般性能测试 2、负载测试 …

【Python】函数 ③ ( 函数返回值定义语法 | 函数返回多个返回值 | 代码示例 )

文章目录 一、函数返回值定义语法二、函数返回多个返回值三、函数返回值代码示例 一、函数返回值定义语法 在 Python 函数中 , 通过 return 关键字 , 可以返回一个结果给调用者 , 这个返回结果就是 函数返回值 ; def 函数名(函数参数):"""函数文档字符串&#…

【C++】---模板初阶(超详练气篇)

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

几种神经网络整定PID参数原理剖析及simulink案例仿真

目录 前言 1 基于单神经元自适应PID Simulink仿真分析 1.1 原理简介 1.1.1 无监督的Hebb学习 ​1.1.2 有监督的Delta学习 1.1.3 有监督的Hebb学习 1.1.4 改进的有监督Hebb学习 1.1.5 总结 1.2 simulink仿真分析 1.2.1 将权值作为状态变量仿真分析 1.2.2 利用局部变量…

Day6 不要二、把字符串转换成整数

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C/C相关题解 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 选择题1. 计算机组成原理 编程题1. 不要二2. 把字符串转换成为整数 选择题 1. 计算机组成原理 题目&#xff1a…

Atcoder beginner contest 303

A - Similar String AC代码&#xff1a; #include<iostream> #include<algorithm> #include<cstring> using namespace std; int main() {int n;cin >> n;string s, t;cin >> s >> t;bool flag true;for (int i 0; i < n; i) {if …

Nginx-Host绕过复现

目录 环境搭建&#xff1a; 第一种处理方式 第二种处理方式 第三种处理方式 原理依据&#xff1a;Nginx与PHP对Host处理方式不同 环境搭建&#xff1a; 1、提前安装完成nginxphpmysql&#xff0c;然后上传文件pwnhub到nginx/html下 2、修改nginx.conf配置文件&#xff1…

集权攻击系列:如何利用PAC新特性对抗黄金票据?

黄金票据简介 黄金票据是一种常见的域内权限维持手段&#xff0c;这种攻击主要是利用了Kerberos认证过程中TGT票据由KRBTGT用户的hash加密的特性&#xff0c;在掌握KRBTGT用户密码之后可以通过签发一张高权限用户的TGT票据&#xff0c;再利用这个TGT向KDC获取域内服务的ST来实…

ChatGPT 使用 拓展资料:2023年6月 吴恩达大咖Deeplearning.ai最新课程

ChatGPT 使用 拓展资料:2023年6月 吴恩达大咖Deeplearning.ai最新课程 Deeplearning.ai刚刚发布几个新的课程https://www.deeplearning.ai/short-courses/?utm_campaign=May%20Short%20Course%20Launch&utm_content=250952287&utm_medium=social&utm_source=link…

2023上半年软件设计师-试题详解与分析

目录 前言 上午题 计算机组成原理 信息安全 计算机相关法律 软件设计 语言处理 操作系统 软件工程 软件测试 面向对象编程 程序设计语言 数据库 数据结构与算法 计算机网络 计算机专业英语 下午题 数据流图 数据库 UML 算法与C语言 面向对象程序设计 前…

4 款原型设计软件助你成为优秀的产品经理

原型设计是产品经理必备的技能。对于产品经理来说&#xff0c;在原型设计的过程中&#xff0c;必然会使用各种原型设计软件。为了保证后续工作的稳定进行&#xff0c;必须满足初始原型设计图纸&#xff0c;而绘制原型设计图纸常用的原型设计软件很多&#xff0c;很多人不知道如…

文心一言 vs GPT4

本周真是科技爱好者的狂欢节。GPT4和文心一言接连发布&#xff0c;AI工具已经开始走进千家万户。 拿文心一言发布会上的几个问题调戏了 GPT4 一下&#xff0c;看看表现如何。 第一个为文心的回答&#xff0c;第二个为GPT4 的回答。 1. 可以总结一下三体的核心内容吗&#xf…

活动预告 | 2023 Meet TVM · 北京站定档,5 场 Talk 你最期待哪一场?

内容一览&#xff1a;2023 Meet TVM 线下聚会第二站定档 6 月 17 日&#xff01;这次我们设定了 5 个 Talk&#xff0c;期待和大家在北京中关村相聚&#xff01; 关键词&#xff1a;编译器 线下活动 2023MeetTVM 3 月 4 日&#xff0c; 2023 Meet TVM 首场线下活动在上海成功举…

awk实战案例

插入新字段 例&#xff1a;在“a b c d”中b的后面插入“e f g” echo "a b c d" | awk {$2$2" e f g";print} 格式化空白 移除每行的前缀、后缀空白&#xff0c;并将各部分左对齐 [rootlocalhost ~]# cat 1.txt aaaa bbb ccccc bbbb …

opencv笔记:高斯滤波和中值滤波对椒盐噪声的处理

目录 1. 椒盐噪声简介 2. 高斯滤波的原理和实现 2.1. 高斯滤波的原理 2.2. 高斯滤波的API 3. 中值滤波的原理和实现 3.1. 中值滤波的原理 3.2. 中值滤波的API 4. 高斯滤波和中值滤波对椒盐噪声的处理结果 数字图像处理中&#xff0c;噪声会导致图像质量下降和信息的丢失&a…

SpringMVC第九阶段:Restful风格实现的CRUD

Restful风格实现的CRUD图书 把前面的传统请求方式的图书的CRUD换成刚刚讲的Restful风格的图书模块的CRUD。只需要修改页面端的请求方式和地址&#xff0c;以及服务器端Controller的接收。 1、列表功能实现 Controller中的修改: RequestMapping(value "/book",me…