Springboot 3.0之Spring Native初体验

这篇具有很好参考价值的文章主要介绍了Springboot 3.0之Spring Native初体验。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Springboot 3.0之Spring Native初体验

Spring 3.0 中引入了一个新特性,即Spring 对Graalvm Image的支持。

Graalvm 官网

https://www.graalvm.org/native-image/

GraalVM编译器

Graalvm 是一个高效能,支持云原生的编译器。支持Java、JavaScript、Python、Ruby、R、WASM等多种语言。编译器的作用就是生成需要更少计算资源的更快、更精简的代码,拿Java 语言举例,Java 代码经过编译后生成class文件,启动Java程序的时候,需要通过JVM虚拟机将class文件加载到JVM内存中运行。现在使用Graalvm 生成Image镜像时,在编译Java代码时会使用 AOT(Ahead-Of-Time),即在编译时直接编译为本机二进制文件,这些文件可立即启动,无需预热即可提供最佳性能。编译完成的二进制文件不需要Java虚拟机即可运行。在不使用Graalvm 的镜像编译功能时,也可以使用Graalvm当作JDK来使用。

Graalvm 架构图[来自官网:https://www.graalvm.org/22.3/docs/introduction/]:

Springboot 3.0之Spring Native初体验

GraalVM为HotSpot Java虚拟机添加了一个高级的即时(JIT)优化编译器,Graalvm 的语言实现框架(Truffle) 可以在JVM上运行JavaScript、Ruby、Python和一些其他支持的流行语言。

Graalvm和JDK的区别:
  • Graalvm 企业对标Oracle JDK,Graalvm 社区版对OpenJDK

  • Graalvm 在基础支持的JDK上又添加了一个高级的JIT编译器,并且这个编译器默认为顶层的JIT编译器,运行时程序正常在JVM加载和执行,编译器将字节码编译为机器码并将其返回JVM时,支持的语言解释器是在Truffle 框架之上编写。

  • Graalvm 支持Native Image,JDK并不支持

  • Graalvm 支持多语言API,即在共享运行中组合编程语言的API(待探究)

Graalvm 目前支持的Java 框架有:
  • Micronaut Java 云原生框架

  • Spring (Spring AOT 插件支持)

  • Helidon (没听过这个)

  • Quarkus Java 云原生框架

Graalvm 目前平台的支持情况:

Community Edition 22.1 by platform.

Feature Linux AMD64 Linux ARM64 macOS macOS ARM64 Windows
Native Image stable stable stable experimental stable
LLVM runtime stable stable stable experimental not available
LLVM toolchain stable stable stable experimental not available
JavaScript stable stable stable experimental stable
Node.js stable stable stable not available stable
Java on Truffle experimental experimental experimental experimental experimental
Python experimental not available experimental not available not available
Ruby experimental experimental experimental experimental not available
R experimental not available experimental not available not available
WebAssembly experimental experimental experimental experimental experimental
JVM 部署模式和原生镜像部署的关键区别
  • 编译为原生镜像时的静态代码分析是从主入口点执行,即 Java 的main方法

  • 无法识别的代码将会被删除,并且不会成为可执行文件的一部分(有点坑)

  • Graalvm 编译时不能识别代码的动态元素,如:JVM的反射机制、Classpath Resource、序列化、动态代理等

  • 应用程序的类路径在生成时是固定的,不能更改

  • 没有所谓的延迟加载(LAZY),所有可执行文件的内容会在程序启动时全部加载到内存中

  • Java 中的一些限制并没有完全受支持

理解Ahead-of-Time

Springboot依赖的就是动态配置很大程度依赖运行时的状态,而Graalvm 在创建NativeImage时,需要在代码编译时对代码进行静态分析,编译成对应的机器码,也就是说,针对于反射、序列化这种依赖于虚拟机的操作,都会被移除。Spring 的Ahead-of-time(AOT插件)就是在代码编译前做一些适配Graalvm的工作,以便Graalvm 能正确解析Springboot的代码,这些提前的工作包括:

  • Spring AOT 生成对应的源代码(需要动态生成的类直接解析生成固定的代码)

  • 字节码的处理,如Spring 中需要动态代理的Bean的处理

  • 依据应用代码生成Graalvm需要的配置文件,告诉Graalvm哪里有反射、资源文件、动态代理等,包括:

    • Resource hints (resource-config.json)

    • Reflection hints (reflect-config.json)

    • Serialization hints (serialization-config.json)

    • Java Proxy Hints (proxy-config.json)

    • JNI Hints (jni-config.json)

以@Configuration 注解举例

@Configuration(proxyBeanMethods = false)
public class MyConfiguration {

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }

}

@Configuration 中配置的@Bean注解,会在程序启动时,由Spring的IOC 容器进行初始化,也就是运行时才创建的Bean对象,当我们创建一个Native image时,Spring就会使用另一种方法去解析这个Bean并创建Bean,Spring AOT 插件会将这个代码做以下处理:

/**
 * Bean definitions for {@link MyConfiguration}.
 */
public class MyConfiguration__BeanDefinitions {

    /**
     * Get the bean definition for 'myConfiguration'.
     */
    public static BeanDefinition getMyConfigurationBeanDefinition() {
        Class<?> beanType = MyConfiguration.class;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
        beanDefinition.setInstanceSupplier(MyConfiguration::new);
        return beanDefinition;
    }

    /**
     * Get the bean instance supplier for 'myBean'.
     */
    private static BeanInstanceSupplier<MyBean> getMyBeanInstanceSupplier() {
        return BeanInstanceSupplier.<MyBean>forFactoryMethod(MyConfiguration.class, "myBean").withGenerator(
                (registeredBean) -> registeredBean.getBeanFactory().getBean(MyConfiguration.class).myBean());
    }

    /**
     * Get the bean definition for 'myBean'.
     */
    public static BeanDefinition getMyBeanBeanDefinition() {
        Class<?> beanType = MyBean.class;
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
        beanDefinition.setInstanceSupplier(getMyBeanInstanceSupplier());
        return beanDefinition;
    }

可以看到上边生成的代码创建的MyConfiguration的类与@Configuration创建的类大致等效,区别就在于,SpringAOT插件生成的代码 是以Graalvm编译器能直接识别的方式创建的,在Spring AOT处理期间,并不会创建Bean的实例,而是在启动时创建。

初体验Spirng 3.0 Native-Image 支持

  1. 准备工作

    环境准备:

    • IDEA 2021.3,具体版本自己看,最好是要支持JDK 17的有些低版本的不支持

    • Maven 3.8.1 Maven 版本要和IDEA兼容,有些不兼容,执行Maven命令会报错,Settings.xml配置,可以暂时取消阿里的Maven仓库镜像,不然会导致无法下载Spring maven 仓库的镜像,因为有些SNAPSHOT版本在阿里仓库没有

    • Graalvm 17 (graalvm-ce-java17-22.1.0,担心和本机JDK冲突的,可以直接在IDEA里配置)

      • cd Graalvm 安装目录

      • gu list 验证是否安装native-image
        Springboot 3.0之Spring Native初体验

      • 没有安装的 执行gu install native-image 命令安装native-image

  2. 代码编写

    HelloService

    public interface HelloService {
    
        String sayHello(String name);
    
        default String sayHello(String prefix,String name){
            return String.format("%s %s",prefix,name);
        }
    }
    
    
    &#x20;ResourceHelloService
    
    ```java
    public class ResourceHelloService implements HelloService{  
    
      private final Resource resource;  
    
      public ResourceHelloService(Resource resource) {  
    
        this.resource = resource;  
    
      }  
    
        @Override  
    
      public String sayHello(String name) {  
    
        try {  
    
          try(InputStream in = this.resource.getInputStream()){  
    
            String prefix = StreamUtils.copyToString(in, StandardCharsets.UTF_8);  
    
      return sayHello(prefix, name);  
    
      }  
    
        }catch (Exception ex){  
    
          throw new IllegalStateException("Failed to read resource " + null, ex);  
    
      }  
    
        }  
    
    }
    

    SimpleHelloService

    public class SimpleHelloService implements HelloService{
    
        @Override
        public String sayHello(String name) {
            return sayHello("Hello", name);
        }
    }
    

    DemoConfiguration

    @Configuration(proxyBeanMethods = false)
    public class DemoConfiguration {
        @Bean
        HelloService helloService() {
            return new SimpleHelloService();
        }
    }
    

    DemoController

    @RestController
    // 一定到导入
    @ImportRuntimeHints(DemoController.DemoControllerRuntimeHints.class)
    public class DemoController {
    
        private final ObjectProvider<HelloService> helloServices;
    
        public DemoController(ObjectProvider<HelloService> helloServices) {
            this.helloServices = helloServices;
        }
    
        @GetMapping("/hello")
        HelloResponse hello(@RequestParam(required = false) String mode) throws Exception {
            String message = getHelloMessage(mode, "Native");
            return new HelloResponse(message);    }
    
        private String getHelloMessage(String mode, String name) throws Exception {
            if (mode == null) {
                return "No option provided";
            } else if (mode.equals("bean")) {
                HelloService helloService = this.helloServices.getIfUnique();
                return (helloService != null) ? helloService.sayHello(name) : "No Bean found";
            } else if (mode.equals("reflection")) {
                String implementationName = Optional.ofNullable(getDefaultHelloServiceImplementation())
                        .orElse(SimpleHelloService.class.getName());
    
                Class<?> implementationClass = ClassUtils.forName(implementationName, getClass().getClassLoader());
                Method method = implementationClass.getMethod("sayHello", String.class);
                Object instance = BeanUtils.instantiateClass(implementationClass);
                return (String) ReflectionUtils.invokeMethod(method, instance, name);
            }
            else if(mode.equals("resource")){
                ResourceHelloService resourceHelloService = new ResourceHelloService(new ClassPathResource("hello.txt"));
                return resourceHelloService.sayHello(name);
            }
            return "Unknown mode: "+mode;
        }
        public record HelloResponse(String message) {
    
        }
    
        private String getDefaultHelloServiceImplementation() {
    
            return null;
        }
    
        static class DemoControllerRuntimeHints implements RuntimeHintsRegistrar{
        // 注册Spring AOT 运行时解析的配置,此代码会被Spring AOT 识别并处理
            @Override
            public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
                hints.reflection().registerConstructor(SimpleHelloService.class.getConstructors()[0], ExecutableMode.INVOKE)
                        .registerMethod(ReflectionUtils.findMethod(SimpleHelloService.class,"sayHello",String.class),ExecutableMode.INVOKE);
                hints.resources().registerPattern("hello.txt");
            }
        }
    }
    
    

    DemoAotNativeApplication

    @SpringBootApplication
    public class DemoAotNativeApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoAotNativeApplication.class, args);
        }
    
    }
    
    
  3. 启动对比验证

    • 启动时间对比

    JVM 运行:maven 的profiles 不要勾选native 然后在IDE 启动应用

Springboot 3.0之Spring Native初体验

Native模式运行:选择maven profile为native 然后点击IDEA 的plugins 中的native:build

Springboot 3.0之Spring Native初体验
Springboot 3.0之Spring Native初体验
编译比较耗时,请耐心等待后。target目录下有一个可执行文件
Springboot 3.0之Spring Native初体验

可执行文件的启动时间非常的短只有0.848s

  1. Spring AOT执行代码对比

    target目录下的spring-aot的文件夹中存在资源文件的描述

Springboot 3.0之Spring Native初体验

反射的资源文件描述

Springboot 3.0之Spring Native初体验

  1. Spring AOT 资源文件查看

    target目录下Spring AOT 自动生成的代码查看

Springboot 3.0之Spring Native初体验

  1. Spring 3.0 DEMO-AOT-NATIVE 项目地址:

参考外国程序员小哥snicoll的项目(也是Spring freamwork的开发人员):https://github.com/snicoll/demo-aot-native.git

补充知识:

不同云原生框架之间的对比

Spring /Micronaut/Quarkus 对比

Spring Native:

优点:

  • 完善的框架,

  • 使用 Spring webFlux 的反应式堆栈

  • 最大的社区

  • 更多的集成

  • 多语言支持

缺点:

  • 大量使用反射

  • 启动时间和内存使用不太适合无服务器云功能

  • 仅对 Graalvm 的实验性支持

Micronaut

优点:

  • 现代云原生框架

  • 反应堆

  • 最小的内存占用和启动时间

  • 编译期间不修改字节码

  • 删除所有级别的反射使用

  • Graalvm / 无服务器云功能

  • 多语言支持(Java grovy Kotlin)

  • 类似于Spring

缺点:

  • 较慢的编译时间 (AOT)

  • 社区比Spring 更小

Quarkus:

优点:

  • 现代云原生框架

  • 反应堆

  • 最小的内存占用和启动时间

  • 基于标准和框架(JAX-RS、Netty、Eclipse Micro profile)

  • Graalvm / Serverless 云功能

  • 个人感觉文档支持较为全面,用起来也比较好用

缺点:

  • 预览中的多语言支持 (Kotlin Scala)

  • 较慢的编译时间 (AOT)

目前Spring AOT 也都是在实验阶段,相对于Quarkus 和Micronaut 来说起步应该比较晚,预计等SpringFramework6 和Spring 3.0 正式版发布之后,有更多的开发者使用起来之后才会发展的更快,Quarkus、Micronaut目前来看支持度较好,不过更看好Quarkus框架,感觉文档更全面一些。现在对云原生框架的探索也仅仅停留在能简单用起来的阶段,国内这部分资料也比较少,后边涉及到微服务这些配套组件的集成还需慢慢探索。需要先会用,才能探究其原理。文章来源地址https://www.toymoban.com/news/detail-439698.html

到了这里,关于Springboot 3.0之Spring Native初体验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Native 实现 0.059s 启动一个SpringBoot项目!

    最近自己用Spring Cloud Alibaba做了一个微服务架构的项目,部署的时候遇到了难题:内存不够。目前该项目有7个微服务,因为我只有一台阿里云的服务器(2C 4G),所以我只能把所有的微服务部署在一台服务器上,部署方式是使用docker制作springboot的fat jar镜像,每个微服务在不加任

    2024年02月12日
    浏览(52)
  • SpringBoot教程(三) | Spring Boot初体验

    上篇文章我们创建了SpringBoot 项目,并且进行了简单的启动。整个项目了里其实我们就动了两个文件,一个是pom.xml负责管理springboot的相关依赖,一个是springBoot的启动类。 pom文件中通过starter的形式大大简化了配置,不像以前一样需要引入大量的依赖配置,搞不好还得解决冲突

    2024年01月16日
    浏览(36)
  • 鸿蒙3.0应用开发体验

    鸿蒙os3.0发布以来,华为官方开始主推ets+arkui开发模式,逐渐抛弃java,为以后去安卓化做铺垫,但目前在笔者体验来看,仍需要大力完善,还有很长的路要走! 什么是ets?ts是js的超集,而ets是ts的超集!ets后缀的文件中可以使用鸿蒙SDKapi的能力,就这么简单!而arkui则与Flu

    2024年02月09日
    浏览(34)
  • 探索React Native认证实战示例项目:打造安全的移动应用体验

    项目地址:https://gitcode.com/hezhii/react-native-auth-example 在移动开发领域,React Native以其跨平台和高效性能而备受青睐。如果你正在寻找一个直观的、基于React Native的身份验证实现示例,那么这个项目—— react-native-auth-example ,将会是你的理想之选。 react-native-auth-example 是一个简单

    2024年04月27日
    浏览(31)
  • 鸿蒙开发工具 DevEco Studio 3.0 体验与项目结构介绍

    HuaWei DevEco Studio 是基于 IntelliJ IDEA Community 开源版本打造,面向全场景多设备,提供一站式的应用/服务集成开发环境(IDE),支持分布式多端开发、分布式多端调测、多端模拟仿真,提供全方位的质量与安全保障。 DevEco Studio 3.0支持 HarmonyOS 3.0 的应用及服务开发,提供了代码

    2024年02月11日
    浏览(31)
  • Spring boot 3.0新特性详解

    Spring Boot 3.0 在 2021 年 9 月发布,该版本带来许多令人兴奋的新特性。本文将详细介绍 Spring Boot 3.0 的主要新特性。 Spring Boot 3.0 要求 JDK 11 或更高版本。并且官方建议使用 Java 16,可以充分利用其新特性。 Spring Boot 3.0 首次官方支持 WebFlux - Spring 的反应式框架。我们可以很容易地开

    2024年02月05日
    浏览(49)
  • Spring Boot 3.0 新书出炉(文末送书)

    大家好,我是哪吒。 很多小伙伴反馈: 空有一腔热血,每天学习20个小时,坚持了两个月,还停在Java基础,感觉什么都会了,又感觉什么都不会,迷迷糊糊; 每天都想着要好好学习SpringBoot,两个月过去了,还没开始; 三天打鱼,两天晒网,哎,一地鸡毛; … SpringBoot知识

    2024年02月04日
    浏览(62)
  • 终于把 Spring Boot 3.0 写成书了!

    大家好,我是R哥。 我的新书《 Spring Boot 3 核心技术与最佳实战 》打磨一年多,今天终于上市了,定价 158 元,今天刚上市搞 5 折促销 ,80 元不到上车,这可能是全网最便宜的时候了,机会难得,想拥抱 Spring Boot 3.0 的不要错过。 文章还没发,已经有老铁粉丝上车了,真爱啊

    2023年04月19日
    浏览(30)
  • spring boot 2.7 -> 3.0升级指南

    spring boot提供一个版本迁移指南 2.7 - 3.0

    2024年02月09日
    浏览(38)
  • Spring Boot 3.0系列【23】应用篇之集成Spring WebFlux

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.4 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 官方文档地址 Spring MVC 是 Spring 专门为 Servlet API 和 Servlet 容器而设计的 Web 框架, 在 5.0 版本中加入了基于响应式的 Web 框架 Spring WebFlux ,它是完全 非阻

    2023年04月14日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包