Skip to content

Spring Boot with GraalVM

Andy Wilkinson edited this page Apr 22, 2024 · 52 revisions

Known GraalVM Native Image limitations

General

  • Signed JARs (used for example in the Azure Java SDK) are not supported out of the box. See this comment on the spring-framework issue for the proposed workaround.

  • GraalVM with Truffle (Ruby,JavaScript) as a native image does not work with Spring applications because this disables inlining support.

  • Groovy, due to its dynamic nature, is not supported at the moment.

  • If a library dynamically loads a charset that’s not detected during image build, this could result in a UnsupportedEncodingException at runtime. To work around that, you can add the buildArgs.add('-H:+AddAllCharsets') build option to the native build tools build plugin configuration.

  • Coordinated Restore at Checkpoint (CRaC) and GraalVM are not supported in combination. A dependency on org.crac:crac should be avoided as it will make reachable code paths that do not work in a native image.

  • Application context hierarchies are not supported by AOT processing or in a native image.

Kotlin

  • There’s a bug in 1.9.0 of the Kotlin Gradle Plugin that causes additional resource directories to be lost. This breaks native image compilation as resources generated by AOT processing are not included in the native image’s classpath. To work around the problem, apply Kotlin’s Gradle plugin first.

Core container

  • Using beans defined as lambdas or instance suppliers with AOT/native is not yet supported (for example when using a functional router with WebFlux). You can track the related issue spring-framework#29555 where some workarounds are described.

  • Loading external resources using the "jar" protocol is not supported by default, as it’s not enabled by default. You can enable this in your build with the buildArgs.add('--enable-url-protocols=jar') option with the native build tools build plugin.

  • Field injection with @Resource does not work. Please use constructor injection or @Autowired-based field injection instead.

  • To use SpEL expressions in a native image, runtime hints must be provided to enable the reflective calls that the expression requires. spring-framework#29548 is tracking the necessary changes to remove this limitation.

Logging

  • Log4j2 is not supported in native images.

  • Logback is supported, including XML configuration. XML configuration is loaded at build time during AOT processing and translated into a fixed, native-friendly format. As a result, loading different XML configuration at runtime is not supported.

  • Commons Logging should be excluded in Spring Applications as spring-jcl provides its own version and having both leads to duplicated classpath entries.

Testing

  • Mockito is not supported yet, see #32195. You can disable tests which rely on Mockito (this includes tests that use @MockBean and @SpyBean) with @DisabledInNativeImage.

Building Container Images

Web

Web MVC and WebFlux

  • The optional usage of @ModelAttribute as supported by Spring MVC and Spring WebFlux does not allow ahead-of-time inference of the needed data binding reflection hints. As a consequence, it is recommended to explicitly annotate the method parameters with @ModelAttribute where binding is required.

WebJars

  • WebJars are not recommended with native images since the webjars-locator-core dependency involves unsupported resource scanning at runtime, and since WebJars dependencies lead to the inclusion of unneeded resources from META-INF/resources/. Consider alternative approaches like directly shipping the required frontend resources in the web application, potentially by leveraging build plugins like the gradle-node-plugin for Gradle or the frontend-maven-plugin for Maven (see related blog post).

Databases

In-Memory Databases

HSQLDB is not supported in a native image. If you require an in-memory database, please use H2 instead.

Flyway

Automatic detection of Java migrations are not supported in native image, see #33458. You can work around this limitation in Flyway by using a FlywayConfigurationCustomizer to register your Java migrations:

@Component
class JavaMigrationsFlywayCustomizer implements FlywayConfigurationCustomizer {

	@Override
	public void customize(FluentConfiguration configuration) {
		configuration.javaMigrations(new V1__create_table(), new ...);
	}

}

Hibernate

Gradle projects written in Kotlin will fail with a ClassCastException when attempting to configure Hibernate’s bytecode enhancement. HHH-15707 is tracking the problem.

Cassandra

The Datastax Cassandra driver starting with 4.17.0 supports GraalVM’s native image. To get this version of the driver with Spring Boot 3.0.x or 3.1.x, you’ll need to override the cassandra-driver.version property in your build file.

Native libraries needed on Linux / MacOS systems

If org.glassfish.jaxb:jaxb-runtime is used (for example, JPA / Hibernate depends on it), you’ll need to install the libfreetype library on your Linux or MacOS system. That’s because a class inside JAXB depends on javax.imageio, which will initialize the graphics subsystem.

Spring Web Services

Spring-WS does not support AOT and GraalVM Native at the moment.

Spring Cloud

Spring Cloud Azure

To use Spring Cloud Azure with GraalVM, you’ll need to add the spring-cloud-azure-native-reachability dependency.

Further GraalVM tweaking

Using tomcat-embed-programmatic

Motivation

tomcat-embed-programmatic is an experimental Tomcat dependency designed to lower the memory footprint. Using it produces smaller native images.

Maven

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-websocket</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>org.apache.tomcat.experimental</groupId>
  <artifactId>tomcat-embed-programmatic</artifactId>
  <version>${tomcat.version}</version>
</dependency>

Gradle

implementation('org.springframework.boot:spring-boot-starter-web') {
  exclude group: 'org.apache.tomcat.embed', module: 'tomcat-embed-core'
  exclude group: 'org.apache.tomcat.embed', module: 'tomcat-embed-websocket'
}
String tomcatVersion = dependencyManagement.importedProperties['tomcat.version']
implementation "org.apache.tomcat.experimental:tomcat-embed-programmatic:$tomcatVersion"
Clone this wiki locally