技术实战-使用 Micronaut 框架的云原生 Java

技术实战——使用 Micronaut 框架的云原生 Java

改变对服务器端 Java 的认知

2017 年,Java 服务器端领域存在认知问题。随着向微服务和更轻量级(通常是容器化运行时)的转变,开发人员开始注意到传统 Java 应用程序的相对膨胀,这些应用程序打包并部署到 servlet 容器上的共享 Java 虚拟机 (JVM)。Serverless 的出现进一步加速了这种认知。

正是在这段时间里,Object Computing的一个团队开始重新思考如何从头开始设计 Java 框架。结果就是Micronaut框架,这是一个 Java 框架,它采用了一种完全不同的方法,将框架连接方式的计算转移到带有 Java 注释的编译阶段。这完全消除了传统 Java 框架中存在的反射、运行时生成的代理和复杂的动态类加载的需要。

Micronaut 框架于 2018 年 4 月首次公开发布,引发了 Java 领域思维的翻天覆地的变化,并改变了人们对 Java 缓慢和臃肿的看法。许多较新的举措采用了类似的方法:将更多逻辑移至应用程序的构建和编译阶段,以优化应用程序启动并消除反射。

构建时方法的好处是显而易见的:通过在编译期间进行更多计算,框架已经准备好以最佳方式执行。消除反射、动态类加载和代理的运行时生成为我们提供了进一步优化 JIT 和关键的GraalVM 的 Native Image 工具的下游优化机会。由于这种方法,Native Image 不需要额外的配置来对 Micronaut 框架应用程序执行封闭世界的静态分析。

由于 Micronaut 框架和 GraalVM 之间的这种协同作用,Micronaut 框架的联合创始人 Graeme Rocher 加入了Oracle 实验室。Oracle Labs 不仅拥有 GraalVM,而且除了对象计算之外,还为 Micronaut 框架的持续开发做出了重大贡献。

什么是 Micronaut 框架?

关于 Micronaut 框架的一个常见误解是它是专门为微服务设计的。事实上,Micronaut 框架为一系列应用程序类型 提供了极其模块化的架构!

在其基础上,Micronaut 框架实现了 JSR-330依赖注入规范。该框架提供了许多额外的内置功能,使其成为基于注释的编程模型支持的通用框架的绝佳选择,包括:

Micronaut 有一个基于Netty I/O 工具包的HTTP 服务器和HTTP 客户端。

用户已经采用 Micronaut 框架来构建无服务器应用程序、命令行应用程序,甚至 JavaFX 应用程序。

Micronaut 框架的坚实核心基础为广泛的模块生态系统提供了基础,使 Micronaut 能够解决一系列问题。这种灵活性是 Micronaut 框架在开发人员中迅速普及的原因。以下架构图描述了框架的结构:

技术实战——使用 Micronaut 框架的云原生 Java

基础层基于 Java Annotation Processing (APT),实现编译时依赖注入,支持各种模块的构建,包括基于 Netty 的 HTTP 服务器。但它也涵盖了其他领域,例如数据访问、安全性和 JSON 序列化。

为什么要使用 Micronaut 框架?

Micronaut 框架的目标是通过完全消除这些框架的动态部分来提供传统 Java 框架的轻量级替代方案,这些动态部分使用 Java 反射、动态类加载以及代理和字节码的运行时生成等特性。

消除传统框架的这些方面对提高性能、内存消耗、安全性、健壮性、易于调试和测试具有深远的影响。与其他解决方案不同的是,Micronaut 框架应用程序也可以在 JVM 中快速启动!

启动时间的改进通常完全消除了在集成和单元测试之间拆分代码的需要,从而大大提高了代码测试周期时间。过去,我们经常因为应用程序启动太慢而编写较少的集成测试。Micronaut 框架消除了这种担忧,因此不包括用于 HTTP 层的大量模拟工具。许多框架这样做是为了避免必须启动应用程序的成本。

消除反射还有助于减少堆栈跟踪的大小,这在传统框架中通常是巨大的。

Micronaut 框架还提供了将您自己的代码转换为构建时方法的机制和 API。这就是为什么通过直接与 Java 编译器集成,Micronaut 框架可以并且确实在注释使用不正确时产生编译错误,从而提高代码的类型安全性和整体开发人员体验。

Micronaut 框架入门

本节将介绍如何开始使用 Micronaut 框架构建云原生 Java 微服务。

有几种不同的方法可以使用 Micronaut 框架。至少,您需要 Java SE 8 或更高版本的 JDK。要使用 Native Image 功能,您需要 GraalVM JDK for Java 11 或更高版本。

要创建 Micronaut 应用程序,您可以使用集成到您喜欢的 IDE 中的向导之一,例如IntelliJ IDEA Ultimate或GraalVM Tools for Micronaut Extension for VSCode。

或者,通过 Web 使用Micronaut Launch创建一个新的 Micronaut 应用程序非常容易。这是一个项目创建向导,可让您选择要构建的应用程序类型和要包含的功能。然后它会生成一个 ZIP 文件,其中包含您可以下载的应用程序,或者让您将代码推送到您选择的 Github 存储库。

如果您对命令行更熟悉,您还可以通过 SDKMAN!、Homebrew 等常用方法安装 Micronaut CLI 来创建应用程序。安装后,创建新应用程序非常简单:

mn create-app demo –build gradle

但是,如果您不热衷于安装额外的 CLI 应用程序,没问题!您可以直接通过 curl使用Micronaut Launch API :

curl https://start.micronaut.io/demo.zip?build=gradle -o demo.zip && unzip demo.zip && cd demo

上述命令使用 Gradle 构建工具创建应用程序。您可以将单词“gradle”替换为“maven”以使用 Maven。

在项目结构方面,Micronaut 框架项目的结构与任何其他 Java 项目相同:

一个新创建的项目具有几个 Java 源代码来帮助您开始工作。第一个是Application.java位于其中的类src/main/java,其中包括 Micronaut 应用程序的主入口点:

package demo;
 
import io.micronaut.runtime.Micronaut;
 
public class Application {
 
   public static void main(String[] args) {
       Micronaut.run(Application.class, args);
   }
}

调用Micronaut.run(..)触发框架的启动序列。

第二个生成的类在src/test/java目录中,并测试应用程序是否可以成功启动而没有任何错误:

package demo;
 
import io.micronaut.runtime.EmbeddedApplication;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
 
import jakarta.inject.Inject;
 
@MicronautTest
class DemoTest {
 
   @Inject
   EmbeddedApplication<?> application;
 
   @Test
   void testItWorks() {
       Assertions.assertTrue(application.isRunning());
   }
}

此 JUnit 5 测试使用 注释@MicronautTest,这是一个 JUnit 5 扩展,它允许 JUnit 5 测试将任何组件注入测试本身。在这种情况下,它是EmbeddedApplication为正在运行的应用程序建模的类型。

为 Micronaut 开发设置 IDE

一般来说,Micronaut 框架基于 Java 注释处理 (APT) 的优点之一是不需要任何特殊的构建工具来使用该框架。所有流行的 IDE 都支持注释处理,但有些(例如 Eclipse)要求您显式启用注释处理。

话虽如此,随着 Micronaut 框架的普及,IDE 供应商已经开发出对该框架的特定支持。JetBrain 的 IntelliJ Ultimate为框架用户提供了出色的工具,包括项目向导、用于配置的代码完成、Micronaut 数据支持等。

此外,Visual Studio Code 通过免费的GraalVM Extension Pack提供了出色的支持,该扩展包基于 NetBeans IDE。它包括一个 Micronaut 项目创建向导、用于配置的代码完成以及用于 Micronaut 应用程序的集成本机图像功能。

一旦您安装了这些选项中的任何一个,只需在 IDE 中打开 Gradle 或 Maven 项目即可设置所有内容,然后您就可以开始了。

编写 REST API

Micronaut 框架支持广泛的服务器端工作负载,包括 REST、gRPC、GraphQL 和消息驱动的微服务,以及 Kafka、RabbitMQ、JMS 和 MQTT 等消息传递技术。本介绍将重点介绍使用默认的基于 Netty 的 HTTP 服务器构建 REST 应用程序。

Micronaut 应用程序中的每个 HTTP 路由都由一个带有注解的 Java 类定义@Controller。注释的名称源自模型视图控制器 MVC 模式。带有注释的类型@Controller可以指定一个或多个映射到特定 HTTP 动词和 URI 的方法。

规范的“Hello World”示例可以使用 Micronaut 控制器实现,如下所示:

package demo;
 
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
 
@Controller("/hello")
public class HelloController {
 
   @Get(uri="/{name}", produces=MediaType.TEXT_PLAIN)
   String hello(String name) {
       return "Hello " + name;
   }
}

控制器映射到 URI /hello。使用 @Get 注释的单个方法处理 HTTP GET 并使用 RFC 5741 URI 模板来绑定name方法的参数。您可以通过从 IDE 运行类的main方法来启动服务器,或者使用. 现在,您可以通过向 Micronaut HTTP 服务器使用的默认 8080 端口发送 curl 请求来手动测试端点:Application./gradlew run./mvnw mn:run

curl -i http://localhost:8080/hello/John
HTTP/1.1 200 OK
date: Mon, 28 Mar 2022 13:08:54 GMT
Content-Type: text/plain
content-length: 10
connection: keep-alive

Hello John

然而,由于 Micronaut 框架非常注重测试,有什么比测试更好的方法来试用 API?以下是上面 HelloController 示例的简单 JUnit 5 测试:

package demo;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
 
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
 
@MicronautTest
public class HelloControllerTest {
 
   @Test
   void testHello(@Client("/") HttpClient client) {
       var message = client
       .toBlocking()
       .retrieve("/hello/John");
       assertEquals("Hello John", message);
   }
}

上述测试注入 Micronaut 的 HTTP 客户端并向 /hello/JohnURI 发送 GET 请求,断言结果正确。

Micronaut 框架的巨大优势之一是执行上述测试的速度非常快,可以与常规单元测试相媲美。即使@MicronautTest 注释启动了 Micronaut 服务器并且测试执行了一个完整的 HTTP 请求/响应周期,测试执行速度也不会受到影响。无需为 HTTP 服务器学习广泛的模拟 API!这鼓励开发人员编写更多的集成测试,以提供显着的长期可维护性和代码质量优势。

访问数据库

访问数据库是服务器端应用程序中的一种常见活动,许多框架提供了简化以提高该领域的开发人员生产力。Micronaut 框架也不例外。

Micronaut Data是一个数据库访问工具包,但是:利用与 Micronaut 编译器的集成,Micronaut Data 添加了数据库查询的编译时检查和构建时计算,以提高运行时效率。

与 Spring Data JPA 非常相似,Micronaut Data 允许您使用存储库模式指定 Java 接口,在编译时自动为您实现数据库查询。

Micronaut Data 对存储库接口的方法签名执行编译时分析,并在可能的情况下实现该接口。否则会出现编译错误。

Micronaut Data 支持多种不同的数据库和查询格式,包括:

涵盖 Micronaut 框架中所有不同的数据库访问选项本身就是一个系列文章。幸运的是,有一些关于该主题的优秀指南:查看“使用 Micronaut Data JDBC 访问数据库”或“使用 Micronaut Data Hibernate/JPA 访问数据库”以获得入门帮助。

我个人最喜欢的是 Micronaut Data JDBC,它是一个简单的 JDBC 数据映射器。它基于编译时 bean 内省,完全消除了持久层的反射。

将Gradle 或 Maven 构建配置为包含 Micronaut Data JDBC后,您可以创建映射到数据库表、视图或查询结果的 Java 17 记录。这与 JPA 不同,JPA 鼓励在 Java 类和表之间进行一对一的映射,并通过使用关联对模式进行完全建模。这些关联引入了诸如延迟加载之类的概念,这些概念通常会导致性能挑战(例如臭名昭著的 N+1 选择问题)。以下是一个示例记录定义:

package demo;
 
import io.micronaut.core.annotation.Nullable;
import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;
 
@MappedEntity
public record Person(
   @Id
   @GeneratedValue
   @Nullable Long id,
   String firstName,
   String lastName,
   int age
) {}

然后,您可以在实现构建应用程序所需的大部分通用逻辑的接口中定义存储库逻辑。例如:

package demo;
 
import java.util.List;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;
 
@JdbcRepository(dialect=Dialect.H2)
public interface PersonRepository extends CrudRepository {
   Person findByAgeGreaterThan(int age);
 
   List findByLastNameStartsWith(String str);
}

上面的示例通过 CrudRepository 超级接口提供了一整套创建、读取、更新和删除操作(CRUD)。它还使用支持的查询表达式定义自定义查询。

如果您有任何需要更高级用例的自定义需求,您可以编写自定义查询、条件查询或直接编写 JDBC 逻辑来绑定结果。Micronaut Data JDBC 让它变得轻而易举,同时完全没有反射和运行时生成的代理。没有像 JPA 中那样的状态和会话同步概念,这有助于保持您的应用程序超轻量级,并在 GraalVM Native Image 中表现出色。

此外,在编译时检查每个存储库接口。这可以防止存储库方法查询不存在的属性或使用不受支持的返回类型,从而在启用如此出色的动态功能的同时保持 Java 的类型安全。

构建本机可执行文件

Micronaut 框架在 GraalVM 公开可用之前首次发布。尽管如此,这两种伟大技术之间的自然协同作用主要是因为 GraalVM 的 Native Image 组件可以轻松地将 Micronaut 应用程序转换为本机可执行文件。

GraalVM Native Image 对 Java 中的反射、运行时代理和动态类加载有很好的支持。但是,开发人员确实需要为 Native Image 提供必要的配置,以声明这些使用发生的时间和地点。但是对于 Micronaut 框架,无需声明这些用法,因为 Micronaut 应用程序在框架级别不使用任何这些技术!这使得 GraalVM Native Image 在提前编译 (AOT) 期间的封闭世界分析变得更加简单。

当然,如果您使用依赖于反射的第三方库,那么您需要声明用法。但是,您使用的框架中的大多数情况都是无反射的,这对您有很大帮助。

Micronaut Gradle和Micronaut Maven插件利用 Oracle Labs 出色的GraalVM Native Build Tools 项目来帮助简化构建本机可执行文件。因此,使用 Gradle 构建本机可执行文件非常简单:

./gradlew nativeCompile

Maven 的等价物是:

./mvnw package -Dpackaging=native-image

每个命令都会在每个工具的构建目录中为它们运行的 平台生成一个本机可执行文件。

运行本机可执行文件将展示使用本机的第一个巨大好处:

./demo
 __  __ _                                  _
|  /  (_) ___ _ __ ___  _ __   __ _ _   _| |_
| |/| | |/ __| '__/ _ | '_  / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_
|_|  |_|_|___|_|  ___/|_| |_|__,_|__,_|__|
  Micronaut (v3.4.0)

[main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 23ms. Server Running: http://localhost:8080

启动时间减少到仅仅几毫秒(上例中为 23 毫秒),内存消耗显着下降。通过如此巨大的减少,可以将 Micronaut 应用程序部署到内存限制更有限的环境或启动时间很关键的情况下(例如,无服务器工作负载)。

请注意,正在进行进一步的研究,以通过Oracle 实验室开发的Micronaut AOT 项目提供更显着的改进。它在构建本机可执行文件之前对字节码运行额外的静态分析步骤,以优化和消除死代码路径并执行将 YAML 转换为 Java 等任务,以避免在运行时需要 YAML 解析器。

为云而建

除了 Native Image,Micronaut 框架还支持多种不同的打包格式和部署目标,包括:

总体而言,Micronaut 框架提供的功能集使其成为构建云原生 Java 应用程序的绝佳选择,从分布式配置支持到集成服务发现,再到为AWS、Google Cloud、Azure等云提供商提供通用抽象实现的模块,和甲骨文云。这些抽象确保您的应用程序在云提供商之间保持可移植性。

概括

Micronaut 框架为服务器端 Java 工作负载带来了新鲜空气。它提供了一种创新的编译时方法和功能集,使其成为构建现代云原生 Java 应用程序的理想选择。

与 GraalVM Native Image 的紧密集成以及与 Oracle Labs 的 GraalVM 团队的工作关系意味着Micronaut AOT和 Micronaut 序列化(Jackson Databind 的无反射替代方案)等项目继续出现重大创新。

围绕 Micronaut 框架出现了一个充满活力的社区,其中包含许多可提高开发人员生产力的模块,包括 Micronaut Data,其中包括与数据库技术的关键集成。

社区反馈继续推动框架的发展。因此,如果您有任何反馈,请不要犹豫,通过Micronaut 社区分享有关新功能和改进的想法。

展开阅读全文

页面更新:2024-03-05

标签:时方   框架   注释   反射   应用程序   实战   代码   服务器   数据库   测试   项目   技术

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top