eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Introduction

In this article, we’ll look at the Java Ahead of Time (AOT) Compiler, which is described in JEP-295 and was added as an experimental feature in Java 9.

First, we’ll see what AOT is, and second, we’ll look at a simple example. Third, we’ll see some restrictions of AOT, and lastly, we’ll discuss some possible use cases.

2. What Is Ahead of Time Compilation?

AOT compilation is one way of improving the performance of Java programs and in particular the startup time of the JVM. The JVM executes Java bytecode and compiles frequently executed code to native code. This is called Just-in-Time (JIT) Compilation. The JVM decides which code to JIT compile based on profiling information collected during execution.

While this technique enables the JVM to produce highly optimized code and improves peak performance, the startup time is likely not optimal, as the executed code is not yet JIT compiled. AOT aims to improve this so-called warming-up period. The compiler used for AOT is Graal.

In this article, we won’t look at JIT and Graal in detail. Please refer to our other articles for an overview of performance improvements in Java 9 and 10, as well as a deep dive into the Graal JIT Compiler.

3. Example

For this example, we’ll use a very simple class, compile it, and see how to use the resulting library.

3.1. AOT Compilation

Let’s take a quick look at our sample class:

public class JaotCompilation {

    public static void main(String[] argv) {
        System.out.println(message());
    }

    public static String message() {
        return "The JAOT compiler says 'Hello'";
    }
}

Before we can use the AOT compiler, we need to compile the class with the Java compiler:

javac JaotCompilation.java

We then pass the resulting JaotCompilation.class to the AOT compiler, which is located in the same directory as the standard Java compiler:

jaotc --output jaotCompilation.so JaotCompilation.class

This produces the library jaotCompilation.so in the current directory.

3.2. Running the Program

We can then execute the program:

java -XX:AOTLibrary=./jaotCompilation.so JaotCompilation

The argument -XX:AOTLibrary accepts a relative or full path to the library. Alternatively, we can copy the library to the lib folder in the Java home directory and only pass the name of the library.

3.3. Verifying That the Library Is Called and Used

We can see that the library was indeed loaded by adding -XX:+PrintAOT as a JVM argument:

java -XX:+PrintAOT -XX:AOTLibrary=./jaotCompilation.so JaotCompilation

The output will look like:

77    1     loaded    ./jaotCompilation.so  aot library

However, this only tells us that the library was loaded, but not that it was actually used. By passing the argument -verbose, we can see that the methods in the library are indeed called:

java -XX:AOTLibrary=./jaotCompilation.so -verbose -XX:+PrintAOT JaotCompilation 

The output will contain the lines:

11    1     loaded    ./jaotCompilation.so  aot library
116    1     aot[ 1]   jaotc.JaotCompilation.<init>()V
116    2     aot[ 1]   jaotc.JaotCompilation.message()Ljava/lang/String;
116    3     aot[ 1]   jaotc.JaotCompilation.main([Ljava/lang/String;)V
The JAOT compiler says 'Hello'

The AOT compiled library contains a class fingerprint, which must match the fingerprint of the .class file.

Let’s change the code in the class JaotCompilation.java to return a different message:

public static String message() {
    return "The JAOT compiler says 'Good morning'";
}

If we execute the program without AOT compiling the modified class:

java -XX:AOTLibrary=./jaotCompilation.so -verbose -XX:+PrintAOT JaotCompilation

Then the output will contain only:

 11 1 loaded ./jaotCompilation.so aot library
The JAOT compiler says 'Good morning'

We can see that the methods in the library won’t be called, as the bytecode of the class has changed. The idea behind this is that the program will always produce the same result, no matter if an AOT compiled library is loaded or not.

4. More AOT and JVM Arguments

4.1. AOT Compilation of Java Modules

It’s also possible to AOT compile a module:

jaotc --output javaBase.so --module java.base

The resulting library javaBase.so is about 320 MB in size and takes some time to load. The size can be reduced by selecting the packages and classes to be AOT compiled.

We’ll look at how to do that below, however, we’ll not dive deeply into all the details.

4.2. Selective Compilation with Compile Commands

To prevent the AOT compiled library of a Java module from becoming too large, we can add compile commands to limit the scope of what gets AOT compiled. These commands need to be in a text file – in our example, we’ll use the file complileCommands.txt:

compileOnly java.lang.*

Then, we add it to the compile command:

jaotc --output javaBaseLang.so --module java.base --compile-commands compileCommands.txt

The resulting library will only contain the AOT compiled classes in the package java.lang.

To gain real performance improvement, we need to find out which classes are invoked during the warm-up of the JVM.

This can be achieved by adding several JVM arguments:

java -XX:+UnlockDiagnosticVMOptions -XX:+LogTouchedMethods -XX:+PrintTouchedMethodsAtExit JaotCompilation

In this article, we won’t dive deeper into this technique.

4.3. AOT Compilation of a Single Class

We can compile a single class with the argument –class-name:

jaotc --output javaBaseString.so --class-name java.lang.String

The resulting library will only contain the class String.

4.4. Compile for Tiered

By default, the AOT compiled code will always be used, and no JIT compilation will happen for the classes included in the library. If we want to include the profiling information in the library, we can add the argument compile-for-tiered:

jaotc --output jaotCompilation.so --compile-for-tiered JaotCompilation.class

The pre-compiled code in the library will be used until the bytecode becomes eligible for JIT compilation.

5. Possible Use Cases for AOT Compilation

One use case for AOT is short running programs, which finish execution before any JIT compilation occurs.

Another use case is embedded environments, where JIT isn’t possible.

At this point, we also need to note that the AOT compiled library can only be loaded from a Java class with identical bytecode, thus it cannot be loaded via JNI.

6. AOT and Amazon Lambda

A possible use case for AOT-compiled code is short-lived lambda functions where short startup time is important. In this section, we’ll look at how we can run AOT compiled Java code on AWS Lambda.

Using AOT compilation with AWS Lambda requires the library to be built on an operating system that is compatible with the operating system used on AWS. At the time of writing, this is Amazon Linux 2.

Furthermore, the Java version needs to match. AWS provides the Amazon Corretto Java 11 JVM. In order to have an environment to compile our library, we’ll install Amazon Linux 2 and Amazon Corretto in Docker.

We won’t discuss all the details of using Docker and AWS Lambda but only outline the most important steps. For more information on how to use Docker, please refer to its official documentation here.

For more details about creating a Lambda function with Java, you can have a look at our article AWS Lambda With Java.

6.1. Configuration of Our Development Environment

First, we need to pull the Docker image for Amazon Linux 2 and install Amazon Corretto:

# download Amazon Linux 
docker pull amazonlinux 

# inside the Docker container, install Amazon Corretto
yum install java-11-amazon-corretto

# some additional libraries needed for jaotc
yum install binutils.x86_64

6.2. Compile the Class and Library

Inside our Docker container, we execute the following commands:

# create folder aot
mkdir aot
cd aot
mkdir jaotc
cd jaotc

The name of the folder is only an example and can, of course, be any other name.

package jaotc;

public class JaotCompilation {
    public static int message(int input) {
        return input * 2;
    }
}

The next step is to compile the class and library:

javac JaotCompilation.java
cd ..
jaotc -J-XX:+UseSerialGC --output jaotCompilation.so jaotc/JaotCompilation.class

Here, it’s important to use the same garbage collector as is used on AWS. If our library cannot be loaded on AWS Lambda, we might want to check which garbage collector is actually used with the following command:

java -XX:+PrintCommandLineFlags -version

Now, we can create a zip file that contains our library and class file:

zip -r jaot.zip jaotCompilation.so jaotc/

6.3. Configure AWS Lambda

The last step is to log into the AWS Lamda console, upload the zip file and configure out Lambda with the following parameters:

  • Runtime: Java 11
  • Handler: jaotc.JaotCompilation::message

Furthermore, we need to create an environment variable with the name JAVA_TOOL_OPTIONS and set its value to:

-XX:+UnlockExperimentalVMOptions -XX:+PrintAOT -XX:AOTLibrary=./jaotCompilation.so

This variable lets us pass parameters to the JVM.

The last step is to configure the input for our Lambda. The default is a JSON input, which cannot be passed to our function, therefore we need to set it to a String which contains an integer, e.g. “1”.

Finally, we can execute our Lambda function and should see in the log that our AOT compiled library was loaded:

57    1     loaded    ./jaotCompilation.so  aot library

7. Conclusion

In this article, we saw how to AOT compile Java classes and modules. As this is still an experimental feature, the AOT compiler isn’t part of all distributions. Real examples are still rare to find, and it will be up to the Java community to find the best use cases for applying AOT.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)