GraalVM and Spring Applications

The opening statement of this question is a bit vague so it's hard to address it properly.

GraalVM absolutely can compile Spring applications. GraalVM distibution is very similar to a normal JDK, it includes a javac utility, a java utility, which can be added to the path and used normally. You can set up the $JAVA_HOME environment variable to point to the directory you unpacked the GraalVM distribution, add $JAVA_HOME/bin to the path, and build Spring applications the way you usually build them, with Maven or Gradle, or any other build tools.

GraalVM can also run Spring applications, compiled both by itself and other JVMs. If you're curious, here's an example of a Spring application that not only runs on GraalVM, but also uses R to visualize a plot of the data, using GraalVM polyglot capabilities.

Now, I guess what your meant is the ability of GraalVM to create executable native images of some Java programs.

Update: November 17th 2019

Some Spring applications work as GraalVM native images. There's active work on making the support better by Pivotal and GraalVM teams. Here's a session by Sébastien Deleuze from Devoxx Belgium 2019 about state of the Spring applications and GraalVM native images where he shows a small hello world Spring application working as a native image and vanilla Spring Petclinic demo using JPA and in-memory database working as a native image: https://www.youtube.com/watch?v=3eoAxphAUIg

You can follow the instructions here: https://github.com/spring-projects-experimental/spring-graalvm-native to build or investigate the samples.

Note this project is experimental as it is noted in its README as well.

The support for the native image is not optimized yet and it will get better, currenly if I try the spring-petclinic-jpa example from this repository it can start in around 200 ms on my not powerful macbook:

14:13:11.990 [main] INFO  o.s.s.petclinic.PetClinicApplication - 
             Started PetClinicApplication in 0.171 seconds (JVM running for 0.173)

Previous Update: May 17th 2019

Here's the spring-framework's wiki page for GraalVM native image support.

The spring-graalvm-native experimental project, created by Andy Clement, shows how it is possible to run a Spring Boot application out of the box as a GraalVM native image. It could be used as a basis for a potential upcoming official support.

All in all, you might try it, but things might not work fully as expected.

Previous answer is below:

There's a spring-fu project, an experimental Kotlin micro-framework based on functional configuration intended to test new ideas for future Spring Boot releases, which is currently experimenting with being able to get compiled to native images by GraalVM.

At the same time, GraalVM team is investigating what can be done to simplify compiling Spring apps to native images and to support more Spring apps than currently. Some of the limitations will remain, so you'll always be able t construct a Spring app that won't work as a GraalVM native image, but perhaps you'll be able to construct Spring apps which will work too.
The exact roadmap of these changes is currently unclear.

Here's a SpringFramework issue tracker ticket that one can follow to see the development.


As Oleg Šelajev already stated, native compilation of Spring Boot apps with GraalVM Native Image (which is a sub project of GraalVM) is possible with limitations right now and is planned to be released with the Spring Framework’s 5.3 release in autumn 2020. With Native Image you're able to achieve similar advantages in memory footprint and startup time reduction as you get with using Quarkus.io, Micronaut etc. I was able to reduce memory footprint from around 500MB to 30MB and startup time from 1.5 seconds to 0.08 seconds in an example project implementing a Reactive Spring Boot Web app.

In short, if you want to use that feature in production you have to wait for the final Spring 5.3 release in late 2020 and the Spring Boot release which is based upon it. If you want to already start using that feature experimentally, you can start right now.

asciicast

====== Updated to spring-graalvm-native 0.7.0 release at June 10, 2020. =======

Here are the basic steps (June 2020), derived from the latest docs of the spring-projects-experimental project spring-graalvm-native and this blog post I recently wrote (step 7 & 8 could be either achieved with a compile.sh bash script or with the help of the native-image-maven-plugin - both alternatives are explained below):

  1. Write your Spring Boot app (like starting over at https://start.spring.io or use an existing application)
  2. Upgrade to a recent Release of Spring Boot 2.3.x (do not start with 2.2 or lower!) inside your pom.xml
  3. Setting start-class element in pom.xml

The native-image command later needs the exact fully qualified class name of your @SpringBootApplication annotated class. Define it in your pom.xml's properties like this:

<properties>
        ...
        <start-class>io.jonashackt.springbootgraal.SpringBootHelloApplication</start-class>
</properties>
  1. Disabling usage of GCLIB proxies

As GraalVM doesn't support GCLIB proxies, Spring Boot needs to use JDK proxies instead. Therefore use the proxyBeanMethods = false property of your @SpringBootApplication class:

@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {
    ...
}
  1. Install GraalVM 20.1.0 and GraalVM Native Image command

The easiest way to do this is to use SDKMAN:

curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 20.1.0.r11-grl
gu install native-image

Check if both works correctly by typing java -version (GraalVM should be listed) and native-image --version. See this blog post for more details.

  1. Relocating Annotation classpath scanning from runtime to build time & 6. Detecting autoconfiguration

Both steps are done for you by the Spring Graal @AutomaticFeature used later with the native-image command. As the @AutomaticFeature was already released on Spring Milestones repository, we can simply add a dependency to our pom.xml (don't forget to also add Spring Milestones repository for now, since this isn't shipped via Maven Central right now):

<dependencies>
        <dependency>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-graalvm-native</artifactId>
            <version>0.7.1</version>
        </dependency>
        ...
        <dependencies>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>
  1. Preparing native-image command execution

In essence, we need to prepare configuration variables for native-image command, then build the app, expand the Spring Boot fat JAR & configure the classpath. I created a compile.sh that executes the necessary steps with bash:

#!/usr/bin/env bash

echo "[-->] Detect artifactId from pom.xml"
ARTIFACT=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.artifactId}' \
--non-recursive \
exec:exec);
echo "artifactId is '$ARTIFACT'"

echo "[-->] Detect artifact version from pom.xml"
VERSION=$(mvn -q \
  -Dexec.executable=echo \
  -Dexec.args='${project.version}' \
  --non-recursive \
  exec:exec);
echo "artifact version is '$VERSION'"

echo "[-->] Detect Spring Boot Main class ('start-class') from pom.xml"
MAINCLASS=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${start-class}' \
--non-recursive \
exec:exec);
echo "Spring Boot Main class ('start-class') is '$MAINCLASS'"

echo "[-->] Cleaning target directory & creating new one"
rm -rf target
mkdir -p target/native-image

echo "[-->] Build Spring Boot App with mvn package"
mvn -DskipTests package

echo "[-->] Expanding the Spring Boot fat jar"
JAR="$ARTIFACT-$VERSION.jar"
cd target/native-image
jar -xvf ../$JAR >/dev/null 2>&1
cp -R META-INF BOOT-INF/classes

echo "[-->] Set the classpath to the contents of the fat jar & add the Spring Graal AutomaticFeature to the classpath"
LIBPATH=`find BOOT-INF/lib | tr '\n' ':'`
CP=BOOT-INF/classes:$LIBPATH
  1. Crafting the native-image command and run the compilation

Now we have mostly everything prepared to craft and finally run the native-image command. Here's an example, which is based on the mentioned example project implementing a Reactive Spring Boot Web app. This one is tricky right now and is dependend on the kind of Spring Boot app you want to compile as GraalVM Native Image! Therefor the best way is to get some inspiration from the example projects of the spring-graal-native project:

GRAALVM_VERSION=`native-image --version`
echo "[-->] Compiling Spring Boot App '$ARTIFACT' with $GRAALVM_VERSION"
time native-image \
  -H:+TraceClassInitialization \
  -H:Name=$ARTIFACT \
  -H:+ReportExceptionStackTraces \
  -Dspring.native.remove-unused-autoconfig=true \
  -Dspring.native.remove-yaml-support=true \
  -cp $CP $MAINCLASS;

There is also a comprehensive explanation in the latest docs or this blog post for every parameter.

Finally execute the bash script via ./compile.sh and grab a coffee! This takes some time depending on your hardware! On my late MBP 2017 this takes around 3-4 minutes for the example project. If everything went fine, you'll find your natively compiled Spring Boot app in /target/native-image/spring-boot-graal. Simply run it with:

./target/native-image/spring-boot-graal

==============================

Alternative to 7 & 8: native-image-maven-plugin

Alternatively to the bash script (and described steps 7 & 8) there's also the native-image-maven-plugin. But please only use it, if you're really sure how to configure the native-image command - since it's execution is quite cumbersome right now (I'am sure there will be many improvements until late 2020). If you want to use the plugin, the steps instead of 7 & 8 are as follows:

  1. Add spring-context-indexer dependency

As the Spring @AutomaticFeature doesn't automatically explore the needed Spring Components while used by the native-image-maven-plugin (is this a bug?), we need to explicitely add the spring-context-indexer to do the job:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
    </dependency>

It creates a target/classes/META_INF/spring.components file which then is picked up by the native image compilation process.

  1. Add native-image-maven-plugin as Maven build profile

In order to get the native-image-maven-plugin working, it is good practice to create a new Maven profile for the native image compilation (see this pom.xml for a fully working example):

<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.nativeimage</groupId>
                    <artifactId>native-image-maven-plugin</artifactId>
                    <version>20.1.0</version>
                    <configuration>
                        <buildArgs>-H:+TraceClassInitialization -H:+ReportExceptionStackTraces -Dspring.native.remove-unused-autoconfig=true -Dspring.native.remove-yaml-support=true</buildArgs>
                        <imageName>${project.artifactId}</imageName>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>native-image</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

We need to add the spring-boot-maven-plugin again, because it prepares neccessary configuration for the native image plugin.

The crucial part is the buildArgs tag which needs to inherit the parameters for the native-image command as seen in the compile.sh script. Compared to that one we can leave out the -cp $CP $MAINCLASS parameter, since the plugin recognises the classpath including the mainclass itself (the latter only, if the start-class tag from step 3 is set). Using <imageName>${project.artifactId}</imageName> is a good idea in order to use our artifactId for the resulting executable image name.

Now simply execute the Maven profile via:

mvn -Pnative clean package

If the compilation succeeded, start your native Spring Boot app with:

./target/spring-boot-graal

==============================

If you want to run the native image compilation on a CI server like TravisCI or use Docker to do the compilation I could recomment this so answer and this blog post. See the full compile process in action on TravisCI also.

Tags:

Spring

Graalvm