开始学习使用Quarkus

581 阅读14分钟

开始使用Quarkus

Java语言是一种用于应用开发的广泛语言。然而,由于需要掌握的句法术语的损失,它对初学者来说是一个挑战。这种损失使初学者感到困惑。

好消息是,SpringBoot、Micronaut、Quarkus等框架都集成了易于使用的库。这些库提供了注释,可以快速而毫不费力地用简短的注释来替换代码块。

Lombok是这些库中的一个例子。"Project Lombok",正如它所知道的,是一个简化Java的库。例如,它通过使用一个或多个注解,省去了Getters、Setters或Equal方法的编写,以及其他许多方法。此外,该类有一个功能齐全的构建器,自动记录变量,等等。本文讨论并允许人们在Quarkus项目中获得Lombok的实践经验。

主要收获

在本文结束时,读者应该很好地掌握了以下内容。

  • 如何在Quarkus项目中设置Lombok?
  • 如何在Quarkus中设置测试来测试Lombok项目的功能
  • 如何处理Lombok中的获取器和设置器
  • Lombok中的访问级别和构造函数
  • Lombok中的EqualsAndHashCode和ToString注解
  • 排除和数据注解
  • 访问器和NotNull
  • Lombok中的Builder、BuilderDefault和Singular注解
  • Slf4j、Lombok配置和自定义日志

前提条件

要跟上这篇文章,读者应该具备以下条件。

  • Java语言的前期知识
  • 机器上安装了Java SDK。建议使用最新的SDK版本。
  • IntelliJ的终极版本是首选,因为它在商店里有很多工具。
  • 一个良好的网络连接,以获取额外的资源。

至于要求,自文章发表以来,先决条件可能随着时间的推移而改变。我们使用JDK版本17 ,VS Code版本1.61 ,以及IntelliJ Ultimate版本2021.2.3 ,用于截图。

为Lombok设置IDE

IntelliJ Ultimate设置

IDE需要Lombok插件,以便在Quarkus项目中有效地与Quarkus一起工作。

进入Settings/plugins 选项,然后搜索 "Lombok "来安装该插件。它看起来会如下图所示。

Lombok IntelliJ plugin

安装成功后,重新启动IDE。

为VS代码设置

至于VS代码,请确保它已将Quarkus工具作为一个扩展安装。该扩展显示如下。VS Code Quarkus tools

  • 搜索 "Lombok "并安装VS代码的Lombok注释。在下面的图片中查看其外观。Lombok VS Code support

  • 重新启动它以确保它反映出变化。

创建一个Lombok Quarkus项目

Quarkus Lombok项目 IntelliJ

创建一个新的项目,结构如下。

  • 名称:Lombok-tutorial
  • :org.gs
  • 工件:Lombok-tutorial

New Quarkus project in IntelliJ

  • 在下一个窗口,不要添加任何依赖项

Visual Studio Code中的Quarkus Lombok项目

安装完扩展后,请按照以下步骤操作。

  • 打开命令调色板。通过进入视图/命令调色板选项来实现。
  • 搜索Quarkus
  • 选择 "生成一个Quarkus项目 "选项

New Quarkus project in VS Code

  • 设置以下内容。
    • 构建工具:Maven
    • GroupId: org.gs
    • 工件标识:lombok-tutorial
    • 没有扩展

Quarkus.io启动器中的Quarkus Lombok项目

  • 前往Quarkus.io starter网站。
  • 为该项目设置以下内容。
    • ArtifactId: lombok-tutorial
    • 群组: org.gs

New Quarkus Lombok project using Quarkus.io starter

  • 下载生成的zip代码,解压后用IDE或代码编辑器打开。

文件夹结构

以下是项目的文件夹结构。

.
├── Lombok-tutorial
│   └── src
│     ├── main
│         ├── docker
│         ├── Java
│         └── resources
│             └── META-INF
│     └── test
│   ├── .dockerignore
│   ├── .gitignore
│   ├── mvnw
│   ├── mvnw.cmd
│   ├── pom.xml
│   ├── Lombok-tutorial.iml
│   └── README.md
  • 删除以下文件夹内的文件。Java,resources, 和test

创建一个 "电影 "示例项目

在java文件夹内创建一个新的Movie.java 文件,并在该文件中添加以下片段。

import java.net.URL;

/**
* The type Movie.
  */
  public class Movie {
  private Long id;
  private String title;
  private String description;
  private String country;
  private int rating;
  private URL officialSite;
  private String language;

  /**
  * Instantiates a new Movie.
    */
    public Movie(Long id, String title, String description, String country, int rating, URL officialSite, String language) {
    this.id = id;
    this.title = title;
    this.description = description;
    this.country = country;
    this.rating = rating;
    this.officialSite = officialSite;
    this.language = language;
    }

  //An empty constructor
  public Movie() {

  }

  //Set Getters and Setters
  public Long getId() {
  return id;
  }

  public void setId(Long id) {
  this.id = id;
  }

  public String getTitle() {
  return title;
  }

  public void setTitle(String title) {
  this.title = title;
  }

  public String getDescription() {
  return description;
  }

  public void setDescription(String description) {
  this.description = description;
  }

  public String getCountry() {
  return country;
  }

  public void setCountry(String country) {
  this.country = country;
  }

  public int getRating() {
  return rating;
  }

  public void setRating(int rating) {
  this.rating = rating;
  }

  public URL getOfficialSite() {
  return officialSite;
  }

  public void setOfficialSite(URL officialSite) {
  this.officialSite = officialSite;
  }

  public String getLanguage() {
  return language;
  }

  public void setLanguage(String language) {
  this.language = language;
  }
  }

pom.xml 文件中,添加下面的代码,在依赖部分下添加Jupiter JUnit包。这个依赖性允许测试项目和运行额外的测试单元。

<!--junit-jupiter.version-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>${junit-jupiter.version}</version>
    <scope>test</scope>
</dependency>

在dependencies部分之前添加下面这段代码。这一部分包含一些变量,说明所使用的依赖关系的版本。例如,JUnit 的版本是5.8.1 。首先,重新加载IDE或项目以应用所做的修改,然后在下一步,在test 文件夹中创建一个新文件。将其命名为MovieTest.java 文件。

<!--dependecies versions used-->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<junit-jupiter.version>5.8.1</junit-jupiter.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

现在重新加载IDE或项目以应用所做的修改,然后在下一步,在test 文件夹中创建一个新文件。将其命名为MovieTest.java 文件。在该文件中,粘贴以下代码。

/**
* The type Movie test.
  */
  class MovieTest {

  private Movie movie;

  /**
  * Sets up.
  *
  * @throws MalformedURLException the malformed url exception
    */
    @BeforeEach
    void setUp() throws MalformedURLException {
    movie = new Movie();
    movie.setTitle("The Lord of the Rings: The Fellowship of the Ring");
    movie.setDescription("In the second age of Middle-earth, the lords of Elves...");
    movie.setCountry("New Zealand - United States");
    movie.setLanguage("English");
    movie.setRating(8);
    movie.setOfficialSite(new URL("https://www.lordsoftherings.com"));
    movie.setId(1L);
    }

// Test the getId method
  @Test
  void getId() {
  assertNotNull(movie);
  assertEquals(1L, movie.getId());
  }

  /**
  * Sets id.
    */
     // Test the setId method
    @Test
    void setId() {
    assertNotNull(movie);
    movie.setId(2L);
    assertEquals(2L, movie.getId());
    }

 // Test the getTitle method
  @Test
  void getTitle() {
  assertNotNull(movie);
  assertEquals("The Lord of the Rings: The Fellowship of the Ring", movie.getTitle());
  }

 // Test the setTitle method
  @Test
  void setTitle() {
  assertNotNull(movie);
  movie.setTitle("The Fellowship of the Ring");
  assertEquals("The Fellowship of the Ring", movie.getTitle());
  }

 // Test the getDescription method
  @Test
  void getDescription() {
  assertNotNull(movie);
  assertEquals("In the second age of Middle-earth, the lords of Elves...", movie.getDescription());
  }

 // Test the setDescription method
  @Test
  void setDescription() {
  assertNotNull(movie);
  movie.setDescription("The lord of Elves, in the second age...");
  assertEquals("The lord of Elves, in the second age...", movie.getDescription());
  }

 // Test the getCountry method
  @Test
  void getCountry() {
  assertNotNull(movie);
  assertEquals("New Zealand - United States", movie.getCountry());
  }

 // Test the setCountry method
  @Test
  void setCountry() {
  assertNotNull(movie);
  movie.setCountry("NZ - US");
  assertEquals("NZ - US", movie.getCountry());
  }

 // Test the getRating method
  @Test
  void getRating() {
  assertNotNull(movie);
  assertEquals(8, movie.getRating());
  }

 // Test the setRating method
  @Test
  void setRating() {
  assertNotNull(movie);
  movie.setRating(9);
  assertEquals(9, movie.getRating());
  }

  // Test the getOfficialSite method
  @Test
  void getOfficialSite() {
  assertNotNull(movie);
  assertEquals("https://www.lordsoftherings.com", movie.getOfficialSite().toString());
  }

  /**
  * Sets official site.
  *
  * @throws MalformedURLException the malformed url exception
    */
    // Test the setOfficialSite method
    @Test
    void setOfficialSite() throws MalformedURLException {
    assertNotNull(movie);
    movie.setOfficialSite(new URL("http://www.lordsoftherings.com"));
    assertEquals("http://www.lordsoftherings.com", movie.getOfficialSite().toString());
    }

  // Test the getLanguage method
  @Test
  void getLanguage() {
  assertNotNull(movie);
  assertEquals("English", movie.getLanguage());
  }

  // Test the setLanguage method
  @Test
  void setLanguage() {
  assertNotNull(movie);
  movie.setLanguage("EN");
  assertEquals("EN", movie.getLanguage());
  }
  }

用IDE运行测试。如果运行选项不可见,右击测试文件,选择运行选项。结果将如下所示。

Running the tests

添加Lombok依赖项

pom.xml 文件中,在依赖项部分添加以下几行代码。这些将允许应用程序利用项目Lombok注释。

<!-- Require lombok for the project -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>

在属性部分(在properties 标签下),指定项目中使用的Lombok的版本。通过添加下面这行代码来完成。

<!-- Specify the version to be used -->
<lombok.version>1.18.20</lombok.version>

再重新加载一次应用程序。这行代码允许IDE为开发目的获取项目的依赖性。

Lombok的特点

获取器和设置器

这些将减少项目的代码。它通过替换注解使用的Getters和Setters方法来实现。Lombok在一个单独的文件中添加场景方法,保持代码的简洁和直接。删除最初在Movie.java 文件中创建的Getters和Setters方法,然后在Movie类上面添加以下代码。

// Create getters and setters methods
@Getter @Setter

接下来,重新运行测试以确定代码结构是否没有改变。如果它们都运行成功,说明代码的结构没有改变。此刻,如果你前往target 文件夹,打开Movie 类,你会发现所有的Getters和Setter方法都在这里自动声明,但在代码中使用。

Project Lombok依赖性使用实时(JIT)实现,只在需要之前创建所有这些方法,例如,在运行之前。

因此,生成的方法将在运行实际项目或测试后看到。

Getters和Setters将被应用于类中的所有项目,因为它们已经被添加到类之前。然而,如果它们只需要一个项目,它们将只应用于该项目之前。

NoArgsConstructor 和 AllArgsConstructor

这些都是用来为程序创建构造函数的。在Movie.java 文件中,有两种类型的构造函数可用。它们是No-argument和All-argument构造函数。如下所示。

无参数构造器
/**
* Instantiates a new Movie.
*/
// No argument constructor
public Movie() {

    }
所有参数构造器
// All argument constructor
public Movie(Long id, String title, String description, String country, int rating, URL officialSite, String language) {
this.id = id;
this.title = title;
this.description = description;
this.country = country;
this.rating = rating;
this.officialSite = officialSite;
this.language = language;
}
  • NoArgsConstructorAllArgsConstructor 注释添加到GettersSetters 注释下面的代码中。
// Generate the NoArgsConstructor and AllArgsConstructor
@NoArgsConstructor
@AllArgsConstructor

删除最初从文件中创建的No argumentall arguments 构造函数。

注意:不要删除在测试文件中发现的构造函数。

  • 重新运行测试。这证明所有的运行都符合预期。
  • 检查目标文件夹中的Movie.class 文件是否有自动添加到其中的代码。

访问级别

它用于描述程序中的项目如何被访问。例如,私下的、公开的、受保护的,等等。此外,在Setters和Getters默认不应用于其他变量的情况下,这个注释很有帮助。比如说。

  • private Long id; 行的上方,添加以下内容。
// Generate Setters with the private access level
@Setter(AccessLevel.PRIVATE)
  • private URL officialSite; 行的上方,添加以下内容。
// Generate Setters with the protected access level
@Setter(AccessLevel.PROTECTED)
  • 在代码中添加另一个变量,称为分钟,如下图所示。
// Variable declaration and initialization
private final int minutes = 120;
  • 在它上面,添加以下内容。
import lombok.Getter;

// Generate Getters and Setters with the private access level
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)

在这种情况下,将弹出以下错误。

Runtime error

该变量由于其访问级别而无法被Project Lombok访问。因此,解决这类错误最简单的方法是删除访问级别或将其访问级别类型改为none

  • 从long类型的id中删除访问级别。
  • 重新运行测试。

ToString

ToString 注解用于生成 方法,该方法返回类的名称,后面是所有字段的顺序,用逗号分隔。toString()

  • 将注解ToString 添加到Java Bean中,就在类的上方。
  • 重新运行测试。
  • 如果成功的话,前往在目标文件夹中找到的Movies.java 文件。
  • 找到自动生成的ToString 方法。它看起来如下所示。
//Return class name, all fields separated by commas in a string form
public String toString() {
Long var10000 = this.getId();
return "Movie(id=" + var10000 + ", title=" + this.getTitle() + ", description=" + this.getDescription() + ", country=" + this.getCountry() + ", rating=" + this.getRating() + ", officialSite=" + this.getOfficialSite() + ", language=" + this.getLanguage() + ", minutes=" + this.getMinutes() + ")";
}

EqualsAndHashCode

  • 像其他注解一样,将EqualsAndHashCode 添加到Movie类中。
// Generate the Equals and HashCode methods
@EqualsAndHashCode

EqualsAndHashCode 注解用于生成 equals(Object other) 和 hashcode() 方法。默认情况下,这些方法使用所有存在的字段作为其参数。

  • 运行测试
  • 检查目标文件夹中的Movie.class 文件的变化。
排除选项

这不是一个功能,而是一个功能的选项。如果想要或需要,一些字段可以被排除在函数中使用或包含在内。这种排除是通过使用exclude 选项。例如,在Movies.java 文件中,在ToStringEqualsAndHashCode 注释中加入exclude 选项,如下图所示。

// Don't include minutes variable in the ToString, Equals and HashCode methods
@ToString(exclude = "minutes")
@EqualsAndHashCode(exclude = "minutes")

记住,分钟被包括在上面创建的初始ToString 函数中。

  • 重新运行测试。
  • 检查目标文件夹中的Movie.class 文件的变化。注意分钟字段被排除在ToStringEqualsAndHashCode 函数之外。

数据

Data 注解会自动生成Getters和Setters、ToString和EqualsAndHashCode方法。转到 src 文件夹中的Movie.java 文件,删除并以Data 注解替换所有GettersSetters,ToString, 和EqualsAndHashCode 注解。

// Generates the Getters and Setters, ToString, and EqualsAndHashCode methods
@Data
  • 重新运行测试。
  • 检查目标文件夹中的Movie.class 文件的变化。注意到这个注释取代了其他的注释,但它却非常短。

NotNull

这个注解确保参数或字段不会以空值传递。在id、title和rating字段之前添加NotNull 注解。下面是一个例子。

// Ensure that the parameter or field is not passed as an empty value
@NotNull
private Long id;

在类中创建一个新的函数,通过将分钟字段中持有的值添加到传递给它的参数中来计算出总和。

// Simple function that adds an input to the minutes variable
int getMinutesPlusX(@NotNull int x){
return this.minutes + x;
}

由于它在传递给它的参数附近有NotNull 注释,它将不允许该参数作为一个空白字段传递。

  • 运行测试
  • 检查目标文件夹中的Movie.class 文件的变化。注意到注释将不允许传递空值,即使是最近创建的函数,正如在目标文件夹中找到的Movie.class 文件中生成的代码所见。
// Annotation won't allow null values
public int getMinutesPlusX(@NotNull int x) {
Objects.requireNonNull(this);
return 120 + x;
}

访问器

Lombok有一个有趣的注解,名字是Accessors 。这个注解删除了字段getters和setters方法中使用的前缀。它既可以被添加到整个类中,也可以根据需要添加到每个字段中。它有不同的选项,如fluent, chain, 和prefix。

  • 如下图所示,将Accessors 注解添加到id字段中。
// Remove the prefix in fields generated by the Getters and Setters methods
@NotNull
@Accessors(fluent = true)
private Long id;
  • 运行测试

它会产生如下所示的错误。

Can’t find setId Error

  • 前往在目标文件夹中发现的Movie.class 。注意到在字段id 之前的前缀SetGet 已经不存在了。它只是仍然是id 。所以,它是通过利用Java的函数重载能力。
  • 通过在MovieTest.java 文件中用id 替换所有getIdsetId 的实例来解决上面的错误。同时,将id 函数改为不同的名称,例如idFunc()
  • 再次重新运行测试

它返回一个成功信息。

创建者、创建者.默认和单数

Builder 注解做了以下事情。

  • 添加一个具有相同静态方法的静态类
  • 一个空的私有无args构造函数
  • 为每个参数添加一个类似Setter的方法
  • 一个调用该方法并传入每个字段的构建方法。
  • 为每个参数添加一个私有的非静态非最终字段
  • 一个toString方法
  • 一个构建方法,在该类中实例化一个新的构建器实例

为了看清它的功能,请做以下工作。

  • Builder 注解添加到Movie 类中。它看起来像这样。
@Builder
  • 重新运行测试
  • 检查在Movie.class 文件中是否增加了builder() 函数。

在目标文件夹中的Movie.class 文件中,它添加了以下代码块。

// The Annotation creates this builder code block
public static Movie.MovieBuilder builder() {
return new Movie.MovieBuilder();
}

使用创建的builder() 方法做一个测试。按照下面的步骤来做。

  • 删除下面显示的所有代码块。
movie = new Movie();
movie.setTitle("The Lord of the Rings: The Fellowship of the Ring");
movie.setDescription("In the second age of Middle-earth, the lords of Elves...");
movie.setCountry("New Zealand - United States");
movie.setLanguage("English");
movie.setRating(8);
movie.setOfficialSite(new URL("https://www.lordsoftherings.com"));
movie.id(1L);
  • 在同一文件中,现在添加以下代码块。
// It does just like the former code by setting the return values expected
movie = Movie.builder().title("The Lord of the Rings: The Fellowship of the Ring")
.description("In the second age of Middle-earth, the lords of Elves...")
.country("New Zealand - United States")
.language("English")
.rating(8)
.officialSite(new URL("https://www.lordsoftherings.com"))
.id(1L)
.build();

上面的代码使用了与以前的代码相同的返回,但使用了builder() 函数来设置它们。重新运行测试应用程序的任务,看看该代码块是否按预期工作。返回类型是成功的。

在测试运行后生成的Movie.class 文件里面的MovieBuilder类中,人们注意到它里面有以下内容。

private Long id;
private String title;
private String description;
private String country;
private int rating;
private URL officialSite;
private String language;

它缺少名称为minutes 的整数。这种缺乏是因为它使用了final关键字。为了解决这个问题,可以使用Project Lombok提供的一个注释,叫做@Builder.Default 。然后,把它添加到minutes 的上面,如下图所示。

// Use Builder.Default to get through the final keyword
@Getter(AccessLevel.PRIVATE)
@Setter(AccessLevel.PRIVATE)
@Builder.Default
private final int minutes = 120;
  • 像以前一样运行所有的测试。检查是否在Movie.class 文件中产生了一些额外的代码。

下面的代码被添加到类中。

private boolean minutes$set;
private int minutes$value;

连同。

public Movie build() {
int minutes$value = this.minutes$value;
if (!this.minutes$set) {
minutes$value = Movie.$default$minutes();
}

这表明它已经被添加到文件中。

  • 现在,在Movie.java 文件中添加一个名为cast 的字符串列表。如下图所示。
// Add a list of strings with the name 'cast'
private List<String> cast;
  • 运行测试
  • 打开Movie.class 文件

注意它添加了一个方法,传递整个字符串列表,如下图所示。

// This method generated passes the whole list of strings
public Movie.MovieBuilder cast(List<String> cast) {
this.cast = cast;
return this;
}
  • 在重新运行测试之前,在它上面添加以下一行代码。
// This line passes a single string element inside the list of string cast
@Singular("cast")

Singular 注解做了以下事情。

  • 没有生成一个Setter方法,而是生成了2个Getter方法

  • 它设置一个添加单个元素,而另一个添加所有的元素到集合中。

  • 运行所有的测试

  • 查看Movie.class 文件

注意到现在应用程序生成的代码在列表投掷的字符串内添加了一个元素,如下图所示,除此之外。

// Adds an element inside the string of list cast
public Movie.MovieBuilder cast(Collection<? extends String> cast) {

Slf4j、Lombok.config和CustomLog

Slf4j是一个简单的日志框架的抽象。要使用Slf4j,必须添加一个新的Maven依赖项。在pom.xml 文件中,在依赖项部分添加以下内容。

<!--Slf4j dependency-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>

重新加载应用程序以获取该依赖,并将其添加到项目中,然后在Movie.java 文件中添加以下注解。

// Adds the log variables to the class generated
@Slf4j

Lombok将提供一个日志变量来写出日志的开箱。它可以是信息,调试,或错误日志。例如,在getMinutesPlusX 函数中添加下面这行。

// Generate the log variables of the type 'info'
log.info("Adding {}", x);

MovieTest.java 文件中添加下面这一行。

/*
* Add another test to the application
* The expected output is 121 */
  @Test
  void getMinutesPlusX() {
  int minutesPlusX = movie.getMinutesPlusX(1);
  assertNotNull(minutesPlusX);
  assertEquals(121, minutesPlusX);
  }

运行所有的测试,然后检查出在控制台生成的日志中是否有info 类型的日志。它看起来应该如下图所示。

Log of type ‘info’ generated

此外,它还在Movie.class 文件中生成了以下内容。

// Generated line of code
private static final Logger log = LoggerFactory.getLogger(`Movie.class`);

人们可以使用topic 选项来改变被输出的日志的名称。要做到这一点,将使用的注释修改为如下所示。

// Specify the custom topic of all logs generated
@Slf4j(topic="MovieDAO")

再运行一次测试,然后检查输出控制台中的差异。它有一个自定义的名字,而不是默认的。它看起来如下。

Changes in the Log Topic

Lombok允许开发者使用自定义的记录器,如Java Logger。要做到这一点,请遵循以下步骤。

  • 在根目录下添加一个新文件,并将其命名为lombok.config
  • 向其添加以下自定义配置,使其允许应用程序使用Java Logger来记录日志
lombok.log.fieldisstatic = false
lombok.log.custom.declaration = java.util.logging.Logger java.util.logging.Logger.getLogger(NAME)(TOPIC)

为了使用这个配置,用@CustomLog 注解替换@Slf4j(topic="MovieDAO") 注解。修改log.info("Adding {}", x); 行,以给出如图所示的最终输出。

// Modify the getMinutesPlusX function in order to produce logs as required
public int getMinutesPlusX(@NotNull int x){
log.info("Adding " + x);
return this.minutes + x;
}

Custom logging using Java Logger

结论

总而言之,上面的文章提供了深入的见解,为用Lombok开发Quarkus项目设置了一个IDE。因此,我们创建了一个简单的项目来演示Getters和Setters、NoArgsConstructor和AllArgsConstructor、AccessLevel、ToString、EqualsAndHashCode、Data、NotNull、Accessors、Builder和Slf4j的概念和深入讨论。