[Java - Spring Native] Use Gradle to compile Spring Boot Web app with Spring Native and GraalVM Java
Gradle, Spring Native and GraalVM Java
Spring Native provides support for compiling Spring applications to native executables using the GraalVM native-image compiler.
Compared to the Java Virtual Machine, native images can enable cheaper and more sustainable hosting for many types of workloads. These include microservices, function workloads, well suited to containers, and Kubernetes
Gradle is an open-source build automation tool focused on flexibility and performance. Gradle build scripts are written using a Groovy - https://groovy-lang.org/ or Kotlin - https://kotlinlang.org/ DSL. Read about Gradle features - https://gradle.org/features/ to learn what is possible with Gradle.
Sample Project Setup
The completed “RESTful Web Service” guide can be retrieved using the following commands:
1 | git clone https://github.com/spring-guides/gs-rest-service |
Validate Spring Boot version
Spring Native 0.11.1 only supports Spring Boot 2.6.2, so change the version if necessary.
1 | // build.gradle |
Add the Spring AOT plugin
The Spring AOT plugin performs ahead-of-time transformations required to improve native image compatibility and footprint.
1 | // build.gradle |
The plugin provides a number of options to customize the transformations, see spring-aot-configuration - https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#spring-aot-configuration for more details.
Enable native image support
Spring Boot’s Cloud Native Buildpacks - https://docs.spring.io/spring-boot/docs/2.6.2/reference/html/spring-boot-features.html#boot-features-container-images-buildpacks support lets you build a container for your Spring Boot application. The native image buildpack - https://github.com/paketo-buildpacks/native-image can be enabled using the BP_NATIVE_IMAGE
environment variable as follows:
1 | // build.gradle |
tiny
builder allows small footprint and reduced surface attack, you can also use base (the default) or full builders to have more tools available in the image for an improved developer experience.
Additional native-image arguments can be added using the BP_NATIVE_IMAGE_BUILD_ARGUMENTS
environment variable.
Freeze GraalVM version
By default, GraalVM versions will be upgraded automatically by Buildpacks to the latest release. You can explicitly configure Spring Boot Maven or Gradle plugins with a specific version of java-native-image buildpack which will freeze GraalVM version, see related versions mapping. For example, if you want to force using GraalVM 21.3.0, you can configure:
1 | // build.gradle |
Maven Repository
Configure your build to include the required repository for the spring-native dependency, as follows:
1 | // build.gradle |
The Spring AOT plugin also requires a dedicated plugin repository in the pom.xml file for Maven and in the in the settings.gradle(.kts)
for Gradle.
1 | // build.gradle |
Build the native application
On MacOS, it is recommended to increase the memory allocated to Docker to at least 8GB
with allocation to more CPU core. On Microsoft Windows make sure to enable the Docker WSL 2 backend for better performance.
The native application can be built as follows:
1 | gradle nativeCompile |
This creates a executable file rest-service-complete
in build/native/nativeCompile/
directory.
Or
1 | gradle bootBuildImage |
This creates a Linux container to build the native application using the GraalVM native image compiler. By default, the container image is installed locally.
Run the native application
To run the application
1 | build/native/nativeCompile/rest-service-complete |
Or you can use docker the usual way as shown in the following example:
1 | docker run --rm -p 8080:8080 rest-service-complete:0.0.1-SNAPSHOT |
If you prefer docker-compose, you can write a docker-compose.yml
at the root of the project with the following content:
1 | version: '3.1' |
And then run
1 | docker-compose up |
The startup time should be less than 100ms, compared to the roughly 1500ms when starting the application on the JVM.
Now that the service is up, visit http://localhost:8080/greeting
, where you should see:
1 | {"id":1,"content":"Hello, World!"} |
FAQs
‘gu’ tool wasn’t found. This probably means that JDK at isn’t a GraalVM distribution.
1 | gradle nativeCompile |
Specify JAVA_HOME
env to GraalVM directory before run gradle nativeCompile
.
1 | export JAVA_HOME=/Users/cloudolife/.asdf/installs/java/graalvm-21.3.0+java17 |
Native reflection configuration for org.hibernate.dialect.PostgreSQLDialect is missing
1 | org.hibernate.boot.registry.selector.spi.StrategySelectionException: Unable to resolve name [org.hibernate.dialect.PostgreSQLDialect] as strategy [org.hibernate.dialect.Dialect] |
TODO
References
[1] GraalVM - https://www.graalvm.org/
[2] Spring Native documentation - https://docs.spring.io/spring-native/docs/current/reference/htmlsingle
[5] The Apache Groovy programming language - https://groovy-lang.org/
[6] Kotlin Programming Language - https://kotlinlang.org/
[7] Gradle | Gradle Features - https://gradle.org/features/
[8] Get Started with Docker | Docker - https://www.docker.com/get-started
[9] Overview of Docker Compose | Docker Documentation - https://docs.docker.com/compose/