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. Overview

CDI (Contexts and Dependency Injection) is a standard dependency injection framework included in Java EE 6 and higher.

It allows us to manage the lifecycle of stateful components via domain-specific lifecycle contexts and inject components (services) into client objects in a type-safe way.

In this tutorial, we’ll take an in-depth look at CDI’s most relevant features and implement different approaches for injecting dependencies in client classes.

2. DYDI (Do-it-Yourself Dependency Injection)

In a nutshell, it’s possible to implement DI without resorting to any framework at all.

This approach is popularly known as DYDI (Do-it-Yourself Dependency Injection).

With DYDI, we keep application code isolated from object creation by passing the required dependencies into the client classes through plain old factories/builders.

Here’s how a basic DYDI implementation might look like:

public interface TextService {
    String doSomethingWithText(String text);
    String doSomethingElseWithText(String text);    
}
public class SpecializedTextService implements TextService { ... }
public class TextClass {
    private TextService textService;
    
    // constructor
}
public class TextClassFactory {
      
    public TextClass getTextClass() {
        return new TextClass(new SpecializedTextService(); 
    }    
}

Of course, DYDI is suitable for some relatively simple use cases.

If our sample application grew in size and complexity, implementing a larger network of interconnected objects, we would end up polluting it with tons of object graph factories.

This would require a lot of boilerplate code just for creating object graphs. This is not a fully-scalable solution.

Can we do DI any better? Of course, we can. Here’s exactly where CDI comes into the picture.

3. A Simple Example

CDI turns DI into a no-brainer process, boiled down to just decorating the service classes with a few simple annotations, and defining the corresponding injection points in the client classes.

To showcase how CDI implements DI at the most basic level, let’s suppose that we want to develop a simple image file editing application. Capable of opening, editing, writing, saving an image file and so on.

3.1. The “beans.xml” File

First, we must place a “beans.xml” file in the “src/main/resources/META-INF/” folder. Even if this file doesn’t contain any specific DI directives at all, it’s required for getting CDI up and running:

<beans xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
  http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

3.2. The Service Classes

Next, let’s create the service classes that perform the file mentioned above operations on GIF, JPG and PNG files:

public interface ImageFileEditor {
    String openFile(String fileName);
    String editFile(String fileName);
    String writeFile(String fileName);
    String saveFile(String fileName);
}
public class GifFileEditor implements ImageFileEditor {
    
    @Override
    public String openFile(String fileName) {
        return "Opening GIF file " + fileName;
    }
    
    @Override
    public String editFile(String fileName) {
      return "Editing GIF file " + fileName;
    }
    
    @Override
    public String writeFile(String fileName) {
        return "Writing GIF file " + fileName;
    }

    @Override
    public String saveFile(String fileName) {
        return "Saving GIF file " + fileName;
    }
}
public class JpgFileEditor implements ImageFileEditor {
    // JPG-specific implementations for openFile() / editFile() / writeFile() / saveFile()
    ...
}
public class PngFileEditor implements ImageFileEditor {
    // PNG-specific implementations for openFile() / editFile() / writeFile() / saveFile()
    ...
}

3.3. The Client Class

Finally, let’s implement a client class that takes an ImageFileEditor implementation in the constructor, and let’s define an injection point with the @Inject annotation:

public class ImageFileProcessor {
    
    private ImageFileEditor imageFileEditor;
    
    @Inject
    public ImageFileProcessor(ImageFileEditor imageFileEditor) {
        this.imageFileEditor = imageFileEditor;
    }
}

Simply put, the @Inject annotation is CDI’s actual workhorse. It allows us to define injection points in the client classes.

In this case, @Inject instructs CDI to inject an ImageFileEditor implementation in the constructor.

Furthermore, it’s also possible to inject a service by using the @Inject annotation in fields (field injection) and setters (setter injection). We’ll look at these options later.

3.4. Building the ImageFileProcessor Object Graph With Weld

Of course, we need to make sure that CDI will inject the right ImageFileEditor implementation into the ImageFileProcessor class constructor.

To do so, first, we should get an instance of the class.

As we won’t rely on any Java EE application server for using CDI, we’ll do this with Weld, the CDI reference implementation in Java SE:

public static void main(String[] args) {
    Weld weld = new Weld();
    WeldContainer container = weld.initialize();
    ImageFileProcessor imageFileProcessor = container.select(ImageFileProcessor.class).get();
 
    System.out.println(imageFileProcessor.openFile("file1.png"));
 
    container.shutdown();
}

Here, we’re creating a WeldContainer object, then getting an ImageFileProcessor object, and finally calling its openFile() method.

As expected, if we run the application, CDI will complain loudly by throwing a DeploymentException:

Unsatisfied dependencies for type ImageFileEditor with qualifiers @Default at injection point...

We’re getting this exception because CDI doesn’t know what ImageFileEditor implementation to inject into the ImageFileProcessor constructor.

In CDI’s terminology, this is known as an ambiguous injection exception.

3.5. The @Default and @Alternative Annotations

Solving this ambiguity is easy. CDI, by default, annotates all the implementations of an interface with the @Default annotation.

So, we should explicitly tell it which implementation should be injected into the client class:

@Alternative
public class GifFileEditor implements ImageFileEditor { ... }
@Alternative
public class JpgFileEditor implements ImageFileEditor { ... }
public class PngFileEditor implements ImageFileEditor { ... }

In this case, we’ve annotated GifFileEditor and JpgFileEditor with the @Alternative annotation, so CDI now knows that PngFileEditor (annotated by default with the @Default annotation) is the implementation that we want to inject.

If we rerun the application, this time it’ll be executed as expected:

Opening PNG file file1.png

Furthermore, annotating PngFileEditor with the @Default annotation and keeping the other implementations as alternatives will produce the same above result.

This shows, in a nutshell, how we can very easily swap the run-time injection of implementations by simply switching the @Alternative annotations in the service classes.

4. Field Injection

CDI supports both field and setter injection out of the box.

Here’s how to perform field injection (the rules for qualifying services with the @Default and @Alternative annotations remain the same):

@Inject
private final ImageFileEditor imageFileEditor;

5. Setter Injection

Similarly, here’s how to do setter injection:

@Inject 
public void setImageFileEditor(ImageFileEditor imageFileEditor) { ... }

6. The @Named Annotation

So far, we’ve learned how to define injection points in client classes and inject services with the @Inject, @Default , and @Alternative annotations, which cover most of the use cases.

Nevertheless, CDI also allows us to perform service injection with the @Named annotation.

This method provides a more semantic way of injecting services, by binding a meaningful name to an implementation:

@Named("GifFileEditor")
public class GifFileEditor implements ImageFileEditor { ... }

@Named("JpgFileEditor")
public class JpgFileEditor implements ImageFileEditor { ... }

@Named("PngFileEditor")
public class PngFileEditor implements ImageFileEditor { ... }

Now, we should refactor the injection point in the ImageFileProcessor class to match a named implementation:

@Inject 
public ImageFileProcessor(@Named("PngFileEditor") ImageFileEditor imageFileEditor) { ... }

It’s also possible to perform field and setter injection with named implementations, which looks very similar to using the @Default and @Alternative annotations:

@Inject 
private final @Named("PngFileEditor") ImageFileEditor imageFileEditor;

@Inject 
public void setImageFileEditor(@Named("PngFileEditor") ImageFileEditor imageFileEditor) { ... }

7. The @Produces Annotation

Sometimes, a service requires some configuration to be fully-initialized before it gets injected to handle additional dependencies.

CDI provides support for these situations, through the @Produces annotation.

@Produces allows us to implement factory classes, whose responsibility is the creation of fully-initialized services.

To understand how the @Produces annotation works, let’s refactor the ImageFileProcessor class, so it can take an additional TimeLogger service in the constructor.

The service will be used for logging the time at which a certain image file operation is performed:

@Inject
public ImageFileProcessor(ImageFileEditor imageFileEditor, TimeLogger timeLogger) { ... } 
    
public String openFile(String fileName) {
    return imageFileEditor.openFile(fileName) + " at: " + timeLogger.getTime();
}
    
// additional image file methods

In this case, the TimeLogger class takes two additional services, SimpleDateFormat and Calendar:

public class TimeLogger {
    
    private SimpleDateFormat dateFormat;
    private Calendar calendar;
    
    // constructors
    
    public String getTime() {
        return dateFormat.format(calendar.getTime());
    }
}

How do we tell CDI where to look at for getting a fully-initialized TimeLogger object?

We just create a TimeLogger factory class and annotate its factory method with the @Produces annotation:

public class TimeLoggerFactory {
    
    @Produces
    public TimeLogger getTimeLogger() {
        return new TimeLogger(new SimpleDateFormat("HH:mm"), Calendar.getInstance());
    }
}

Whenever we get an ImageFileProcessor instance, CDI will scan the TimeLoggerFactory class, then call the getTimeLogger() method (as it’s annotated with the @Produces annotation), and finally inject the Time Logger service.

If we run the refactored sample application with Weld, it’ll output the following:

Opening PNG file file1.png at: 17:46

8. Custom Qualifiers

CDI supports the use of custom qualifiers for qualifying dependencies and solving ambiguous injection points.

Custom qualifiers are a very powerful feature. They not only bind a semantic name to a service, but they bind injection metadata too. Metadata such as the RetentionPolicy and the legal annotation targets (ElementType).

Let’s see how to use custom qualifiers in our application:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
public @interface GifFileEditorQualifier {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
public @interface JpgFileEditorQualifier {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
public @interface PngFileEditorQualifier {}

Now, let’s bind the custom qualifiers to the ImageFileEditor implementations:

@GifFileEditorQualifier
public class GifFileEditor implements ImageFileEditor { ... }
@JpgFileEditorQualifier
public class JpgFileEditor implements ImageFileEditor { ... }
@PngFileEditorQualifier
public class PngFileEditor implements ImageFileEditor { ... }

Lastly, let’s refactor the injection point in the ImageFileProcessor class:

@Inject
public ImageFileProcessor(@PngFileEditorQualifier ImageFileEditor imageFileEditor, TimeLogger timeLogger) { ... }

If we run our application once again, it should generate the same output shown above.

Custom qualifiers provide a neat semantic approach for binding names and annotation metadata to implementations.

In addition, custom qualifiers allow us to define more restrictive type-safe injection points (outperforming the functionality of the @Default and @Alternative annotations).

If only a subtype is qualified in a type hierarchy, then CDI will only inject the subtype, not the base type.

9. Conclusion

Unquestionably, CDI makes dependency injection a no-brainer, the cost of the extra annotations is very little effort for the gain of organized dependency injection.

There are times when DYDI does still have its place over CDI. Like when developing fairly simple applications that only contain simple object graphs.

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)