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 tutorial, we’ll learn about coupling in Java, including types and a description of each one of them. In the end, we briefly describe the Dependency Inversion principle and the Inversion of Control and how these are related to coupling.

2. Coupling in Java

When we talk about coupling, we describe the degree to which classes within our system depend on each other. Our goal during the development process is to reduce coupling.

Consider the following scenario. We’re designing a metadata collector application. This application collects metadata for us. It fetches metadata in XML format, then exports fetched metadata to a CSV file, and that’s all. An initial approach could be, as we can appreciate in the design:

Metadata collector diagrama tight coupling

Our module takes care of fetching, processing, and exporting the data. However, it’s a lousy design. This design infringes on the single responsibility principle. So, to improve our first design, we need to separate the concerns. After this change, our design looks like this:

Metadata collector diagrama separetion of concerns

Now, our design is decoupled into two new modules, XML Fetch and Export CSV. Meanwhile, the Metadata Collector Module depends on both. This design is better than our initial approach, but it’s still a work in progress. In the following sections, we’ll notice how we can improve our design based on good coupling practices.

3. Tight Coupling

When a group of classes is highly dependent on each other, or we’ve classes that assume a lot of responsibilities is called tight coupling. Another scenario is when an object creates another object for its usage. Tight coupling code is hard to maintain.

So, let us see this behavior using our base application. Let’s jump into some code definitions. First, our XMLFetch class:

public class XMLFetch {
    public List<Object> fetchMetadata() {
        List<Object> metadata = new ArrayList<>();
        // Do some stuff
        return metadata;
    }
}

Next, the CSVExport class:

public class CSVExport {
    public File export(List<Object> metadata) {
        System.out.println("Exporting data...");
        // Export Metadata
        File outputCSV = null;
        return outputCSV;
    }
}

And finally, our MetadataCollector class:

public class MetadataCollector {
    private XMLFetch xmlFetch = new XMLFetch();
    private CSVExport csvExport = new CSVExport();
    public void collectMetadata() {
        List<Object> metadata = xmlFetch.fetchMetadata();
        csvExport.export(metadata);
    }
}

As we can notice, our MetadataCollector class depends on XMLFecth and CSVExport classes. Additionally, it is in charge of creating them.

If we need to improve our collector, maybe add a new data JSON fetcher and export data in PDF format, we need to include these new elements inside our class. Let’s code the new “improved” class:

public class MetadataCollector {
    ...
    private CSVExport csvExport = new CSVExport();
    private PDFExport pdfExport = new PDFExport();
    public void collectMetadata(int inputType, int outputType) {
        if (outputType == 1) {
            List<Object> metadata = null;
            if (inputType == 1) {
                metadata = xmlFetch.fetchMetadata();
            } else {
                metadata = jsonFetch.fetchMetadata();
            }
            csvExport.export(metadata);
        } else {
            List<Object> metadata = null;
            if (inputType == 1) {
                metadata = xmlFetch.fetchMetadata();
            } else {
                metadata = jsonFetch.fetchMetadata();
            }
            pdfExport.export(metadata);
        }
    }
}

The Metadata Collector Module needs some flags to handle new functionalities. Based on the flag value, the respective child module will be instantiated. However, every new functionality not only makes our code more complex but also makes maintenance harder. This is a sign of tight coupling, and we must avoid it.

4. Loose Coupling

During the development process, the number of relationships among all our classes needs to be the fewest possible. This is called loose coupling. Loose coupling is when an object gets the object to be used from external sources. Our objects are independent one each other. Loosely coupled code reduces maintenance effort. Moreover, it provides much more flexibility to the system.

Loose coupling is expressed through the Dependency Inversion Principle. In the next section, we will describe it.

5. Dependency Inversion Principle

The Dependency Inversion Principle (DIP) refers to how high levels modules should not depend on low-level modules for their responsibilities. Both should depend on abstraction.

In our design, this is the fundamental problem. The metadata collector (high-level) module depends on fetch XML and export CSV data(low-level) modules.

But what can we do to improve our design? DIP shows us the solution, but it doesn’t talk about how to implement it. In this case, it is when Inversion of Control(IoC) takes action. IoC indicates the way of defining abstractions between our modules. To sum up, it is the way of implementing DIP.

So, let’s apply DIP and IoC to our current example. First, we need to define an interface for fetching and other for exporting data. Let’s jump into code to see how to do that:

public interface FetchMetadata {
    List<Object> fetchMetadata();
}

Simple, isn’t it? Now we define our exporting interface:

public interface ExportMetadata {
    File export(List<Object> metadata);
}

Moreover, we need to implement these interfaces in the correspondent class. In short, we need to update our current classes:

public class XMLFetch implements FetchMetadata {
    @Override
    public List<Object> fetchMetadata() {
        List<Object> metadata = new ArrayList<>();
        // Do some stuff
        return metadata;
    }
}

Next, we need to update the CSVExport class:

public class CSVExport implements ExportMetadata {
    @Override
    public File export(List<Object> metadata) {
        System.out.println("Exporting data...");
        // Export Metadata
        File outputCSV = null;
        return outputCSV;
    }
}

Additionally, we update the main module’s code to support new design changes. Let’s see how it looks:

public class MetadataCollector {
    private FetchMetadata fetchMetadata;
    private ExportMetadata exportMetadata;
    public MetadataCollector(FetchMetadata fetchMetadata, ExportMetadata exportMetadata) {
        this.fetchMetadata = fetchMetadata;
        this.exportMetadata = exportMetadata;
    }
    public void collectMetadata() {
        List<Object> metadata = fetchMetadata.fetchMetadata();
        exportMetadata.export(metadata);
    }
}

We can observe two main changes in our code. First, the class depends only on abstractions, not concrete types. On the other hand, we remove the dependency on low-level modules. There is no need to keep any logic related to low-level module creation in the collector module. The interaction with those modules is through a standard interface. The advantage of this design is that we can add new modules to fetch and export data, and our collector code won’t change.

By applying DIP and IoC, we improve our system design. By inverting (changing) the control, the application becomes decoupled, testable, extensible, and maintainable. The following image shows us how looks the current design:

Metadata collector diagrama using DIP and IoCFinally, we remove any tightly coupled code from our code base and enhance the initial design with loosely coupled code using DIP with IoC.

6. Conclusion

In this article, we covered coupling in Java. We first went through the general coupling definition. Then, we observed the differences between tightly coupling and loosely coupling. Later, we learned to apply DIP with IoC to get a loosely coupled code. This walk-through was done by following an example design. We could observe how our code improved in each step by applying good design patterns.

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)