文章

Spring Native 快速上手

Spring Native 是一种将 Spring 应用编译为本机可执行文件的技术,无需 Java 虚拟机 (JVM)。这使得 Spring 应用可以实现毫秒级的启动时间、更低的内存占用、更高的性能、更强的安全性以及更易于部署。

Spring Native 通过字节码生成和提前编译技术将 Spring 应用编译为本机可执行文件。字节码生成技术将 Java 字节码转换为本机机器码,而提前编译技术则在编译时而不是在运行时执行某些计算。这使得 Spring Native 应用可以绕过 JVM 的解释和运行过程,直接运行编译后的机器码,从而获得更好的性能。

Spring Native 适用于各种类型的 Spring 应用,包括 Web 应用、微服务、批处理应用和命令行工具。它还支持与各种框架和库集成,包括 MyBatis、Spring Data 和 Spring Security。

本文使用 Spring initializr 快速创建了一个集成 MyBatis 和 Spring Native 的 Spring Boot 应用程序。

前提条件

需要安装 GraalVM 并需要定义以下环境变量:

  • JAVA_HOME
  • GraalVM_HOME

操作步骤

1.创建项目

使用以下命令创建一个带有 MyBatis 和 H2 数据库的 Spring Boot 应用程序。

1
2
3
4
5
6
$ curl -s https://start.spring.io/starter.tgz\
     -d name=mybatis-sample\
     -d artifactId=mybatis-sample\
     -d dependencies=mybatis,h2,native\
     -d baseDir=mybatis-sample\
     | tar -xzvf - && cd mybatis-sample

2.添加 mybatis-spring-native 依赖

打开 pom.xml 文件并添加 mybatis-spring-native 依赖配置。

1
2
3
4
5
<dependency>
  <groupId>org.mybatis.spring.native</groupId>
  <artifactId>mybatis-spring-native-core</artifactId>
  <version>0.1.0-SNAPSHOT</version>
</dependency>

如果你使用 SNAPSHOT 版本,请添加 Sonatype OSS 快照存储库,如下所示:

1
2
3
4
5
<repository>
  <id>sonatype-oss-snapshots</id>
  <name>Sonatype OSS Snapshots Repository</name>
  <url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>

3.新建 SQL 脚本

新建一个 sql 脚本(src/main/resources/schema.sql)来生成 city 表。

1
2
3
4
5
6
7
CREATE TABLE city
(
  id      INT PRIMARY KEY auto_increment,
  name    VARCHAR,
  state   VARCHAR,
  country VARCHAR
);

4.新建 domain 类

新建 City 类(src/main/java/com/example/mybatissample/City.java)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.example.mybatissample;

public class City {

  private Long id;
  private String name;
  private String state;
  private String country;

  public Long getId() {
    return this.id;
  }

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

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

  public void setName(String name) {
    this.name = name;
  }

  public String getState() {
    return this.state;
  }

  public void setState(String state) {
    this.state = state;
  }

  public String getCountry() {
    return this.country;
  }

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

  @Override
  public String toString() {
    return getId() + "," + getName() + "," + getState() + "," + getCountry();
  }

}

5.新建 mapper 接口

创建 com.example.mybatissample.CityMapper 接口类,通过注解驱动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.mybatissample;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CityMapper {

  @Insert("INSERT INTO city (name, state, country) VALUES(#{name}, #{state}, #{country})")
  @Options(useGeneratedKeys = true, keyProperty = "id")
  void insert(City city);

  @Select("SELECT id, name, state, country FROM city WHERE id = #{id}")
  City findById(long id);

}

6.修改 Spring Boot 应用启动类

添加 CommandLineRunner实现,调用 CityMapper 实现添加城市

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.mybatissample;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MybatisSampleApplication {

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

  private final CityMapper cityMapper;

  public MybatisSampleApplication(CityMapper cityMapper) {
    this.cityMapper = cityMapper;
  }

  @Bean
  CommandLineRunner sampleCommandLineRunner() {
    return args -> {
      City city = new City();
      city.setName("San Francisco");
      city.setState("CA");
      city.setCountry("US");
      cityMapper.insert(city);
      System.out.println(this.cityMapper.findById(city.getId()));
    };
  }

}

7.运行 Spring Boot 应用

使用 Spring Boot Maven 插件运行创建的应用程序,本文使用 spring-boot:run 命令启动应用

1
E:\tmp\mybatis-sample>mvn spring-boot:run

启动成功后的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2022-02-10 16:29:41.362  INFO 11824 --- [           main] o.s.nativex.NativeListener               : AOT mode disabled

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.3)

2022-02-10 16:29:41.423  INFO 11824 --- [           main] c.e.m.MybatisSampleApplication           : Starting MybatisSampleApplication using Java 17.0.2 on LAPTOP-Xiaowangye with PID 11824 (E:\tmp\mybatis-sample\target\classes started by Xiaowangye in E:\tmp\mybatis-sample)
2022-02-10 16:29:41.423  INFO 11824 --- [           main] c.e.m.MybatisSampleApplication           : No active profile set, falling back to default profiles: default
2022-02-10 16:29:42.078  INFO 11824 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2022-02-10 16:29:42.204  INFO 11824 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2022-02-10 16:29:42.279  INFO 11824 --- [           main] c.e.m.MybatisSampleApplication           : Started MybatisSampleApplication in 1.156 seconds (JVM running for 1.412)
1,San Francisco,CA,US
2022-02-10 16:29:42.319  INFO 11824 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-02-10 16:29:42.322  INFO 11824 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.091 s
[INFO] Finished at: 2022-02-10T16:29:42+08:00
[INFO] ------------------------------------------------------------------------

8.生成本地可执行文件和可执行 jar

通过以下方式打包本地可执行文件和 jar 文件:

1
E:\tmp\mybatis-sample>mvn package -Pnative -DskipTests

注意:Windows 平台请使用 x64 Native Tools Command Prompt 命令提示符执行

运行 Native Image:

1
E:\tmp\mybatis-sample>target\mybatis-sample
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2022-02-10 15:55:07.745  INFO 13576 --- [           main] o.s.nativex.NativeListener               : AOT mode enabled

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.3)

2022-02-10 15:55:07.755  INFO 13576 --- [           main] c.e.m.MybatisSampleApplication           : Starting MybatisSampleApplication v0.0.1-SNAPSHOT using Java 17.0.2 on LAPTOP-Xiaowangye with PID 13576 (E:\tmp\mybatis-sample\target\mybatis-sample.exe started by Xiaowangye in E:\tmp\mybatis-sample)
2022-02-10 15:55:07.755  INFO 13576 --- [           main] c.e.m.MybatisSampleApplication           : No active profile set, falling back to default profiles: default
2022-02-10 15:55:07.813  INFO 13576 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2022-02-10 15:55:07.816  INFO 13576 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2022-02-10 15:55:07.823  INFO 13576 --- [           main] c.e.m.MybatisSampleApplication           : Started MybatisSampleApplication in 0.098 seconds (JVM running for 0.1)
1,San Francisco,CA,US
2022-02-10 15:55:07.825  INFO 13576 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2022-02-10 15:55:07.826  INFO 13576 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

总结

Spring Native 是一种非常有前景的技术,它可以帮助 Spring 应用在性能、启动时间、内存占用、安全性以及部署方面获得显著的提升。

本文由作者按照 CC BY 4.0 进行授权