借助 Spring Boot 和 GraalVM 实现原生 Java

Java 在主导着企业级应用。但是在云中,采用 Java 的成本要比其竞争者更高。使用 GraalVM 进行原生编译降低了在云中 Java 的成本:它所创建的应用启动更快,使用的内存也更少。

原生编译为 Java 用户带来了很多的问题:原生 Java 会如何改变开发方式?我们在什么情况下该转向原生 Java?在什么情况下又该避免转向原生 Java?要使用原生 Java,我们该采用哪个框架?本系列的文章将回答这些问题。

Java 社区包罗万象

我们知道,Java 是一个神奇的生态系统,很难列出哪些东西是最适合我们的。这样做的话,清单似乎是无穷无尽的。但是,要说出它的几个缺点也不是那么困难。正如本文所说的,在 JRE 上运行的应用往往需要 10 秒钟或更长的时间才能启动,并需要数百或数千兆字节的内存。

这样的性能在如今的世界并不处于领先的位置。有些新的领域和机会正在出现:函数即服务产品、容器化与容器编排。它们有一个共同点,即对启动速度和内存占用有很高的要求。

迈向 GraalVM!

GraalVM 提供了一个前进的方向,但它也有一定的代价。GraalVM 是 OpenJDK 的替代方案,它有一个名为 Native Image 的工具,支持预先(ahead-of-time,AOT)编译。

AOT 编译与常规的 Java 编译有一些差异。正如本文所概述的那样,Native Image 消除了 Java 中“所有不必要的东西”。那么,Native Image 是如何知道 Java 或 Spring Boot 中哪些是不必要的呢?

Native Image 会探查我们的源码,并确定所有可达的代码,也就是通过调用或我们代码的使用所能链接到的代码。其他的所有内容,不管是位于应用的 classpath 下还是位于 JRE 中,都会被视为不必要的,它们都会被抛弃掉。

当我们做了一些 Native Image 无法明确知道该怎么处理的事情时,麻烦就来了。毕竟,Java 是一个非常动态化的语言。我们有可能会创建这样一个 Java 应用:在运行时,将一个字符串编译成文件系统中一个合法 Java 类文件,并将其加载到 ClassLoader 中,然后使用反射创建它的实例或者为其创建代理。我们还可能会将实例序列化到磁盘上,然后将其加载到另外一个 JVM 中。在这个过程中,我们可能不需要链接任何比java.lang.Object更具体的类。但是,如果这些类型没有被放到原生可执行堆中,所有的这些方式在原生 Java 中是无法正常运行的。

但是,我们并没有失去任何东西。我们可以在一个配置文件中告诉 Native Image 要保留哪些类型,这样,在运行时使用反射、代理、classpath 资源加载、JNI 等特性的时候,它依然可以运行。

现在,Java 和 Spring 生态系统非常庞大。所有的东西都要进行配置将会非常痛苦。所以我们有了两种方案:1)教会 Spring 尽可能避免使用这些机制,或者 2)教会 Spring 尽可能多地提供配置文件,这个配置文件必然要包含 Spring 框架和 Spring Boot,并且要在一定程度上包含 Spring Boot 支持的第三方集成功能。友情剧透一下,这两种方案我们都需要!

Spring Native

Spring 团队在 2019 年启动了 Spring Native 项目,为 Spring Boot 生态系统引入了原生可执行程序编译的功能。它已经为多种不同的方式提供了研究场所。但是,Spring Native 并没有从根本上改变 Framework 5.x 或 Spring Boot 2.x。而且它也绝不是终点,只是漫长旅程中的第一步:它已经为下一代 Spring Framework(6.x)和 Spring Boot(3.x)证明了很多概念,这两个版本预计都会在 2022 年晚些时候发布。这些新一代的项目会进行更多的优化,所以前景看起来是非常光明的!鉴于这些版本尚未发布,我们将会在本文中研究一下 Spring Native。

Spring Native 会对发送给 Native Image 的源码进行转换。比如,Spring Native 会将spring.factories服务加载机制转换为静态类,从而使 Spring Native 应用知道要使用它们。它会将所有的 Java 配置类(带有@Configuration注解的类)转换成 Spring 的函数式配置,从而消除应用及其依赖的反射。

Spring Native 还会自动分析我们的代码,探测需要 GraalVM 配置的场景,并以编程的方式提供这些配置。Spring Native 为 Spring、Spring Boot 以及第三方集成提供了线索(hint)类。

第一个 Spring Native 应用:JPA、Spring MVC 和 H2

我们开始使用 Spring Native 的方式与所有其他 Spring 项目相同:访问Spring Initializr,点击 cmd + B(或 Ctrl + B)或者 Add Dependencies,并选择 Spring Native。

Spring Initializr 会配置 Apache Maven 和 Gradle 构建。随后,只需添加必要的依赖即可。我们先从一些典型的依赖开始。将 Artifact 名称改为 jpa,接下来添加如下依赖:Spring Native、Spring Web、Lombok、H2 Database和Spring Data JPA。请确保选择 Java 17,当然你也可以选择 Java 11,但这就像你挥舞着一个橡胶做的小鸡满世界乱跑,这看上去非常傻,对吧?点击“Generate”,解压生成的项目并将其导入到你最喜欢的 IDE 中。

这个样例非常简单,将JpaApplication.java类改成如下所示:

package com.example.jpa;

import lombok.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import javax.persistence.*;
import java.util.Collection;
import java.util.stream.Stream;

@SpringBootApplication
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }
}

@Component
record Initializr(CustomerRepository repository)
       implements ApplicationRunner {

   @Override
   public void run(ApplicationArgument
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值