Skip to main content

Spring

Spring Native provides experimental support of building native images for Spring projects.

This page is based on Spring Native 0.12.0.

warning

Spring Native is still an experimental project. Use with caution.

Getting Started

When creating a new Spring Boot project, the easiest way to enable Spring Native is using Spring initializr. When adding dependencies, make sure Spring Native is selected.

Enable Spring Native

For an existing project, you need to add Spring Native configurations manually. This can be done by comparing a newly created Spring Native project with your existing project. You may only need to update Maven or Gradle build configurations to get it working.

A simple Maven project is used as an example. Full source of this project can be found at GitHub.

Spring Native provides two approaches to build native images, using Buildpacks or Native Build Tools. The difference is that Buildpacks requires Docker to build container images, but Native Build Tools requires local GraalVM to build native executables.

Buildpacks

When using Buildpacks, the following command can be used to build a container image.

./mvnw spring-boot:build-image

The native image buildpack is used to build a container image with native images. The actual builder used is paketobuildpacks/builder:tiny. The BP_NATIVE_IMAGE environment variable enables this buildpack. Extra options can be passed to native-image using the BP_NATIVE_IMAGE_BUILD_ARGUMENTS environment variable.

Spring Boot Maven plugin
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>${repackage.classifier}</classifier>
<image>
<builder>paketobuildpacks/builder:tiny</builder>
<env>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
</env>
</image>
</configuration>
</plugin>

The build result is a container image with the native image of the current Spring project.

Run the container image
docker run --rm -p 8080:8080 grpc:1.0.0-SNAPSHOT

Native Build Tools

When using Native Build Tools, the following command can be used to build a native image.

./mvnw -Pnative package

Maven or Gradle plugin provided by GraalVM is used to invoke native-image tool to build a native image. For Maven projects, the native profile contains the configurations of Native Build Tools Maven plugin.

Profile native
<profile>
<id>native</id>
<properties>
<repackage.classifier>exec</repackage.classifier>
<native-buildtools.version>0.9.9</native-buildtools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native-buildtools.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>test-native</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
</execution>
<execution>
<id>build-native</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

The build result is a native executable in the target directory.

Native Hints

If an application requires specific configurations related to native image generation, it can add hints for Spring Native to update the configurations used by native image builder.

The easiest way to provide native hints is using annotations defined in the org.springframework.nativex.hint package. These annotations are usually added to the Spring Boot application class.

The @NativeHint annotation provides hints. The table below lists attributed related to Native Image configurations. These attributes are used to generate build configurations for native-image. Values of these attributes are other annotation types to provide configuration details.

AttributeTypeDescription
optionsString[]Options passed to native-image
typesTypeHint[]Types that should be accessible
resourcesResourceHint[]Resources to be included
initializationInitializationHint[]Class initialization configurations
jdkProxiesJdkProxyHint[]JDK dynamic proxies
serializablesSerializationHint[]Serialization configurations
aotProxiesAotProxyHint[]Class based proxies that should be created at image build time

Options

Options are passed to the native-image command. These options will be added to the native-image.properties file.

Options
@NativeHint(
options = {
"-H:+ReportExceptionStackTraces"
}
)

Types

@TypeHint annotation configures reflectively accessed elements for Native Image.

The table below shows the attributes.

AttributeTypeDescription
typesClass<?>[]Class references
typeNamesString[]Type names of classes
methodsMethodHint[]Method information for reflective invocation and metadata query
queriedMethodsMethodHint[]Method information for reflective metadata query
fieldsFieldHint[]Fields information
accessTypeAccess[]Scope of the reflection entries or resource access to be configured

TypeAccess is an enum type which describes various types of access that can be requested.

Types
@NativeHint(
types = {
@TypeHint(
types = {io.netty.buffer.AbstractByteBufAllocator.class},
access = TypeAccess.QUERY_DECLARED_METHODS),
@TypeHint(
typeNames = {"io.netty.buffer.AdvancedLeakAwareByteBuf"},
access = TypeAccess.QUERY_DECLARED_METHODS
)
}
)

Resources

@ResourceHint annotation configures the resource patterns to be included.

The table below shows the attributes.

AttributeTypeDescription
patternsString[]Resource patterns
isBundlebooleanUse resource bundles

When providing configurations for resource bundles, the patterns attribute specifies the name of a resource bundle.

Resources
@NativeHint(
resources = {
@ResourceHint(patterns = "input.txt"),
@ResourceHint(patterns = "AppResources", isBundle = true)
}
)

Initialization

@InitializationHint annotation configures the initialization time.

The table below shows the attributes.

AttributeTypeDescription
initTimeInitializationTimeInitialization time of a type
typesClass<?>[]Class references
typeNamesString[]Type names of classes
packageNamesString[]Names of packages

InitializationTime enum type has two values, BUILD and RUN.

Initialization
@NativeHint(
initialization = {
@InitializationHint(
initTime = InitializationTime.BUILD,
packageNames = {"io.netty.util"}
)
}
)

JDK Proxy

@JdkProxyHint annotation configures interfaces of dynamic proxies.

The table below shows the attributes.

AttributeTypeDescription
typesClass<?>[]Interfaces of a dynamic proxy
typeNamesString[]Interface names of a dynamic proxy
JDK Proxy
@NativeHint(
jdkProxies = {
@JdkProxyHint(
types = {java.util.concurrent.Callable.class}
)
}
)

Serialization

@SerializationHint configures the serializable types.

The table below shows the attributes.

AttributeTypeDescription
typesClass<?>[]Class references of serializable types
typeNamesString[]Class names of serializable types
Serialization
@NativeHint(
serializables = {
@SerializationHint(
typeNames = {"com.example.MyClass"}
)
}
)

AOT Proxy

@AotProxyHint configures class based proxies.

The table below shows the attributes.

AttributeTypeDescription
interfacesClass<?>[]Interfaces of a proxy
interfaceNamesString[]Interface names of a proxy
targetClassClass<?>Target class
targetClassNameStringName of target class
proxyFeaturesintProxy features
AOT Proxy
@NativeHint(
aotProxies = {
@AotProxyHint(targetClass = org.lognet.springboot.grpc.FailureHandlingSupport.class)
}
)

Verify

To verify configurations generated by native hints, we can run the spring-aot:generate goal to generate configurations. Configurations can be found in the target/classes/META-INF/native-image/org.springframework.aot/spring-aot directory.

./mvnw spring-aot:generate

Use Tracing Agent

Instead of providing native hints, GraalVM tracing agent can also be used to generate native image configurations.

Run the Application

We can run the application with tracing agent enabled, then interact with the running application. Tracing agent will generate configuration files.

  1. Configure the Spring AOT plugin to use the native-agent mode.
Use native-agent mode
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>${spring-native.version}</version>
<configuration>
<mode>native-agent</mode>
</configuration>
</plugin>
  1. Build the application with the AOT plugin enabled.
  2. Run the application with the tracing agent to generate configuration files.

As shown in the command below, the output directory of tracing agent is set to src/main/resources/META-INF/native-image/io.vividcode.spring-native/grpc, which is the recommended directory for native image build configurations.

java -DspringAot=true \
-agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/io.vividcode.spring-native/grpc \
-jar target/spring-grpc-0.0.1-SNAPSHOT.jar

Test the application with typical scenarios to make sure that as many code paths are executed as possible.

  1. Configure the Spring AOT plugin to use the native mode.
  2. Build the native image using the native profile.
./mvnw package -Pnative
  1. Verify the generated native image.
Combined configurations

By using this approach, we actually combine configurations generated by the tracing agent and those created by Spring AOT plugin. This is necessary when AOT proxies are used, because they are only supported by Spring AOT plugin.

Work with Tests

Running the application with tracing agent is not a good choice, because manual interaction with the application is required to ensure code coverage. A better choice is to integrate with existing tests, including unit tests and integration tests.

Use an Access Filter

When running tests with tracing agent, the agent may intercept invocations related to test classes. These invocations won't happen when running the actual application. It's better to filter out these invocations using filters.

The code below shows a simple access filter.

Access filter
{
"rules": [
{
"excludeClasses": "org.apache.maven.surefire.**"
},
{
"excludeClasses": "net.bytebuddy.**"
},
{
"excludeClasses": "org.apiguardian.**"
},
{
"excludeClasses": "org.junit.**"
},
{
"excludeClasses": "org.mockito.**"
},
{
"excludeClasses": "org.springframework.test.**"
},
{
"excludeClasses": "org.springframework.boot.test.**"
}
]
}

Update Maven Surefire Plugin

Maven Surefire plugin needs to be updated to run tests with tracing agent. In the argument of native-image-agent,

  • Value of the access-filter-file option is set to the access filter JSON file shown above.
  • Value of the config-merge-dir option is set to the target/classes/META-INF/native-image/io.vividcode.spring-native/grpc directory.
Maven surefile plugin configuration
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
-agentlib:native-image-agent=access-filter-file=../test-access-filter.json,config-merge-dir=target/classes/META-INF/native-image/io.vividcode.spring-native/grpc
</argLine>
</configuration>
</plugin>

The config-merge-dir option is used instead of config-output-dir, because there are many tests to run. Configurations from different tests will be merged.

Now we can run tests with mvn test. After all tests are executed, configuration files will be generated into the target/classes directory. These configurations can be combined with those generated by Spring AOT plugin.

Samples

See alexcheng1982/spring-native-samples for Spring Native samples.