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

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

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

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

Browser testing is essential if you have a website or web applications that users interact with. Manual testing can be very helpful to an extent, but given the multiple browsers available, not to mention versions and operating system, testing everything manually becomes time-consuming and repetitive.

To help automate this process, Selenium is a popular choice for developers, as an open-source tool with a large and active community. What's more, we can further scale our automation testing by running on theLambdaTest cloud-based testing platform.

Read more through our step-by-step tutorial on how to set up Selenium tests with Java and run them on LambdaTest:

>> Automated Browser Testing With Selenium

Partner – Orkes – NPI EA (cat=Java)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

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.

eBook – Reactive – NPI(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

1. Introduction

Mono is a central concept in reactive programming, particularly in Project Reactor. It represents a stream that emits at most one item and then completes successfully or with an error. Mono is used for asynchronous operations that return a single result or no result at all.

Mono is particularly useful for representing the result of asynchronous computations, such as database queries, HTTP requests, or any operation that returns a single value or completes without emitting any value.

In this article, we’ll explore the differences between three common ways to create a Mono: just(), defer(), and create().

2. Mono.just()

The Mono.just() method is the simplest way to create a Mono. It takes an existing value and wraps it in a Mono, effectively making it available for subscribers immediately. We use this method when data is available and there’s no need for complex computation or deferred execution.

When we use Mono.just(), the value is evaluated and stored at the moment the Mono is created, rather than waiting until it’s subscribed to. Once created, the value cannot be changed, even if the underlying data source is modified before subscription. This ensures that the value emitted during subscription is always the one that was available at the time of Mono creation.

Let’s see how this behavior works with an example:

@Test
void whenUsingMonoJust_thenValueIsCreatedEagerly() {
    String[] value = {"Hello"};
    Mono<String> mono = Mono.just(value[0]);

    value[0] = "world";

    mono.subscribe(actualValue -> assertEquals("Hello", actualValue));
}

In this example, we can see that Mono.just() creates the value eagerly at the time of Mono creation. The Mono is initialized with the value “Hello” from an array, and even though the array element is modified to “world” before the Mono is subscribed to, the emitted value remains “Hello”.

Let’s explore few common use cases for Mono.just():

  • When we have a known, static value that’s ready to emit
  • It’s ideal for simple use cases that don’t involve any computation or side effects

3. Mono.defer()

Mono.defer() enables lazy execution, meaning the Mono isn’t created until it’s subscribed to. This is especially useful when we want to avoid unnecessary resource allocation or computation until the Mono is actually needed.

Let’s examine an example to verify that the Mono is created when it’s subscribed to, not when it’s initially defined:

@Test
void whenUsingMonoDefer_thenValueIsCreatedLazily() {
    String[] value = {"Hello"};
    Mono<String> mono = Mono.defer(() -> Mono.just(value[0]));

    value[0] = "World";

    mono.subscribe(actualValue -> assertEquals("World", actualValue));
}

In this example, we can see that the value in the array is changed after the Mono is created. Since Mono.defer() creates the Mono lazily, it doesn’t capture the value at creation time but instead waits until the Mono is subscribed to. As a result, when we subscribe, we receive the updated value, “World”, rather than the original value, “Hello”, demonstrating that Mono.defer() defers the evaluation until the moment of subscription.

Mono.defer() is particularly useful in scenarios where we need to create a new, distinct Mono instance for each subscriber. This allows us to generate a separate instance that reflects the current state or data at the moment of subscription. This approach is essential when we need to dynamically derive the value based on conditions that might change between subscriptions.

Let’s look into one scenario where deferred Mono is created based on the method parameter:

public Mono<String> getGreetingMono(String name) {
    return Mono.defer(() -> {
        String greeting = "Hello, " + name;
        return Mono.just(greeting);
    });
}

Here, the Mono.defer() method defers the creation of the Mono until the subscription, using the name passed to the getGreetingMono() method:

@Test
void givenNameIsAlice_whenMonoSubscribed_thenShouldReturnGreetingForAlice() {
    Mono<String> mono = generator.getGreetingMono("Alice");

    StepVerifier.create(mono)
      .expectNext("Hello, Alice")
      .verifyComplete();
}

@Test
void givenNameIsBob_whenMonoSubscribed_thenShouldReturnGreetingForBob() {
    Mono<String> mono = generator.getGreetingMono("Bob");

    StepVerifier.create(mono)
      .expectNext("Hello, Bob")
      .verifyComplete();
}

When called with the argument “Alice”, the method produces a Mono that emits “Hello, Alice”. Similarly, when called with “Bob”, it produces a Mono that emits “Hello, Bob”.

Let’s see some common use cases for Mono.defer():

  • When our Mono creation involves expensive operations like database queries or network calls, using defer(), we can prevent these from being executed unless this result is actually needed
  • It’s useful for generating values dynamically or when the computation depends on external factors or user input

4. Mono.create()

Mono.create() is the most flexible and powerful method, giving us full control over the Mono‘s emission process. It allows us to programmatically generate values, signals, and errors based on custom logic.

The key feature is that it provides a MonoSink. We can use the sink to emit a value with sink.success(). If there’s an error, we use sink.error(). To send a completion signal without a value, we call sink.success() without arguments.

Let’s explore an example where we perform an external operation, such as querying a remote service. In this scenario, we’ll use Mono.create() to encapsulate the logic for handling the response. Depending on whether the service call is successful or results in an error, we’ll emit either a valid value or an error signal to the subscribers:

public Mono<String> performOperation(boolean success) {
    return Mono.create(sink -> {
        if (success) {
            sink.success("Operation Success");
        } else {
            sink.error(new RuntimeException("Operation Failed"));
        }
    });
}

Let’s verify that this Mono emits a success message when the operation succeeds:

@Test
void givenSuccessScenario_whenMonoSubscribed_thenShouldReturnSuccessValue() {
    Mono<String> mono = generator.performOperation(true);

    StepVerifier.create(mono)
      .expectNext("Operation Success")
      .verifyComplete();
}

When the operation fails, the Mono emits an error with the message “Operation Failed“:

@Test
void givenErrorScenario_whenMonoSubscribed_thenShouldReturnError() {
    Mono<String> mono = generator.performOperation(false);

    StepVerifier.create(mono)
      .expectErrorMatches(throwable -> throwable instanceof RuntimeException 
        && throwable.getMessage()
          .equals("Operation Failed"))
      .verify();
}   

Let’s explore a few common scenarios for Mono.create():

  • This method provides the necessary flexibility when we need to implement complex logic for emitting values, handling errors, or managing back-pressure
  • It’s ideal for integrating with legacy systems or performing complex asynchronous tasks that require fine-grained control over emission and completion

5. Key Differences

Now that we’ve explored the three methods individually, let’s summarize their key differences:

Feature Mono.just() Mono.defer() Mono.create()
Execution Time Eager (upon creation) Lazy (upon subscription) Lazy (manual emission)
Value State

Static/predefined value

Dynamic value on each subscription Manually produced value
Use case When data is available and doesn’t change

When data needs to be generated on demand

When integrating with complex logic or external sources
Error Handling No error handling

Can handle errors during Mono creation

Explicit control over success and errors

Performance

Efficient for static, already-known values

Useful for dynamic or expensive operations

Suitable for complex or asynchronous code

6. Conclusion

In this tutorial, we examined three methods for creating Mono, including scenarios where each approach is most suitable.

Understanding the differences between Mono.just(), Mono.defer(), and Mono.create() is key to effectively using reactive programming in Java. By choosing the right approach, we can make our reactive code more efficient, maintainable, and suited to our specific use case.

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.

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

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

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)