20

The GraalVM system apparently cannot compile a Spring application into a native image.

Can we compile a subset of the Spring application -- say, as a separate library -- and then use that with the remainder compiled using the usual javac compiler?

Or maybe if we leave out some Spring features from our application?

Any other possibilities?

Community
  • 1
  • 1
Alok P
  • 994
  • 1
  • 9
  • 18
  • When you say that GraalVM cannot compile a spring application I assume you mean using native-image? – BoriS Jun 19 '18 at 06:00
  • Yes, that's what I meant. Thanks. I've corrected the question now. – Alok P Jul 05 '18 at 05:45
  • There are some recent additions to the Java microservices space which compile to native code, occupy a fraction of the space and equivalent Spring application would take up, and also startup and execute in a fraction of the time. Examples are: Micronaut, Helidon, Quarkus, ... Also, Vert.X can compile to native code. – Alok P Oct 23 '19 at 19:32
  • What about updating the title to « Compiling Spring applications to GraalVM native images » to disambiguate the title as you did for the description? – Sébastien Deleuze Mar 20 '21 at 22:12
  • Thanks for the suggestion; title changed. – Alok P Mar 22 '21 at 00:21

5 Answers5

50

A probably more up-to-date answer to this question is provided by the release of Spring Native beta that we (the Spring team) have just announced with a very detailed blog post and a video.

It allows you to compile Spring applications to a native executable with GraalVM native-image compiler via Spring Boot mvn spring-boot:build-image or gradle bootBuildImage commands, or by just leverage a local native-image installation via the native-image-maven-plugin.

A Spring Boot application compiled as a native image starting in 38 ms

The most useful links to use it are start.spring.io which now provides Spring Native support and the getting started section of the reference documentation .

Spring Native support in available on start.spring.io

Make sure to configure properly the Spring AOT Maven and Gradle plugins that are mandatory to get proper native support for your Spring application.

Enjoy!

Sébastien Deleuze
  • 5,950
  • 5
  • 37
  • 38
44

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: March 13, 2021

Spring Native project is in beta and you can use it. The accepted answer by Sébastien Deleuze is a great starting point to investigate: https://stackoverflow.com/a/66596191/1087978

Update: November 17, 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.

Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25
  • Yes -- I meant "compile into native images". – Alok P Jul 05 '18 at 05:31
  • And I suppose we could always mix compiled-to-native code and JIT-compiled code by using JNI. So the ideal complete system would be one which could somehow separate out the two types of code (compilable-to-native vs. only-JIT-compilable) in the same application, then compile-to-native what can be so compiled, and then automatically and transparently link the two using JNI. – Alok P Jul 05 '18 at 05:39
  • Yes, you can compile Java code as a native library, for example see point 8 in the 10 things post: https://medium.com/graalvm/graalvm-ten-things-12d9111f307d. I'm not 100% sure on the motivation for doing so, but it's possible. – Oleg Šelajev Jul 05 '18 at 09:59
  • Two motivations that come immediately to mind are: speed, copy-protection of commercially distributed software in regions where copyright is not effectively enforced. – Alok P Jul 07 '18 at 22:34
  • Would be useful to make a list of Spring constructs which native-image (GraalVM's compile-to-native-code utility) can handle. – Alok P Jul 07 '18 at 22:43
5

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.

jonashackt
  • 12,022
  • 5
  • 67
  • 124
2

As May 2020, Spring has release Spring Graalvm Native. Spring Graalvm Native

NingLee
  • 1,477
  • 2
  • 17
  • 26
0

For now one can use alternatives like Quarkus, Micronaut and Vert.X which have Spring-compatible features or can be used with Spring. They are all compilable to small native-code binary executables, start up in an instant and have tiny memory footprints.

These new frameworks avoid GraalVM's limitations by interpreting the annotations and other specifications at build time. This way they avoid the run-time features of Java that the GraalVM AOT compiler native-image cannot support.

Alok P
  • 994
  • 1
  • 9
  • 18