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

Before Java 9, the Java Reflection API has a superpower: It could gain access to the non-public class members without limitation. After Java 9, the modular system wants to limit the Reflection API to a reasonable extent.

In this tutorial, we’ll inspect the relationship between the module system and reflection.

2. Modular System and Reflection

Even though reflection and the module system make their appearance at different times in Java’s history, they need to work together to build a reliable platform.

2.1. The Underlying Model

One of the goals of the Java module system is strong encapsulation. The strong encapsulation mainly consists of readability and accessibility:

  • The readability of modules is a coarse concept and concerns whether one module has a dependency on another module.
  • The accessibility of modules is a finer concept and cares if one class can access another class’s field or method. It is provided by class boundary, package boundary, and module boundary.
j1

The relationship between these two rules is that readability comes first, and accessibility builds upon readability. For example, if a class is public but not exported, the readability will prevent further use. And, if a non-public class is in an exported package, the readability will allow its passing, but the accessibility will reject it.

To increase the readability, we can use the “requires” directive in the module declaration, specify the “–add-reads” option on the command line, or invoke the Module.addReads method. In the same way, to break the boundaries encapsulation, we can use the “opens” directive in the module declaration, specify the “–add-opens” option on the command line, or invoke the Module.addOpens method.

Even reflection can’t break the readability and accessibility rules; otherwise, it will lead to corresponding errors or warnings. One thing to note: When using reflection, the runtime will automatically set up a readability edge between two modules. That also implies that if something goes wrong, it’s because of accessibility.

2.2. Different Reflection Use Cases

In the Java module system, there are different module types, for example, named module, unnamed module, platform/system module, application module, and so on:

j2

To be clear, the two concepts “module system” and “system module” may sound confusing. So, let’s use the “platform module” concept instead of the “system module”.

Considering the above module types, there exist quite a few combinations between different module types. Generally, an unnamed module can’t be read by named modules except for automatic modules. Let’s only inspect three typical scenarios where illegal reflective access happens:

j3

In the above picture, deep reflection means using the Reflection API to get access to non-public members of a class by invoking the setAccessible(flag) method. When using reflection to access a named module from another named module, we’ll get an IllegalAccessException or InaccessibleObjectException. Similarly, when using reflection to access an application named module from an unnamed module, we get the same errors.

However, when using reflection to access the platform module from an unnamed module, we’ll get an IllegalAccessException or a warning. And the warning message is useful to help us to find where the problem happens and to make further remedies:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

In the above warning message form, the $PERPETRATOR represents the reflecting class information and the $VICTIM represents the reflected class information. And, this message is attributed to the relaxed strong encapsulation.

2.3. Relaxed Strong Encapsulation

Before Java 9, many third-party libraries utilize the reflection API to do their magic work. However, the strong encapsulation rules of the module system would invalidate most of that code, especially those using deep reflections to access JDK internal APIs. That would be undesirable. For a smooth migration from Java 8 to Java 9’s modular system, a compromise is made: relaxed strong encapsulation.

The relaxed strong encapsulation provides a launcher option –illegal-access to control the runtime behavior. We should note that the –illegal-access option only works when we use reflection to access platform modules from unnamed modules. Otherwise, this option has no effect.

The –illegal-access option has four concrete values:

  • permit: opens each package of platform modules to unnamed modules and shows a warning message only once
  • warn: is identical to “permit“, but shows a warning message per illegal reflective access operation
  • debug: is identical to “warn“, and also prints the corresponding stack trace
  • deny: disables all illegal reflective access operations

From Java 9, the –illegal-access=permit is the default mode. To use other modes, we can specify this option on the command line:

java --illegal-access=deny com.baeldung.module.unnamed.Main

In Java 16, the –illegal-access=deny becomes the default mode. Since Java 17, the –illegal-access option is entirely removed.

3. How to Fix Reflection Illegal Access

In the Java module system, a package needs to be open to allow deep reflection.

3.1. In Module Declaration

If we’re the code author, we can open the package in the module-info.java:

module baeldung.reflected {
    opens com.baeldung.reflected.opened;
}

To be more cautious, we can use the qualified opens:

module baeldung.reflected {
    opens com.baeldung.reflected.internal to baeldung.intermedium;
}

When migrating our existing code to the modular system, for convenience, we can open the whole module:

open module baeldung.reflected {
    // don't use opens directive
}

We should note that an open module doesn’t allow inner opens directives.

3.2. On the Command-Line

If we’re not the code author, we can use the –add-opens option on the command line:

--add-opens java.base/java.lang=baeldung.reflecting.named

And, to add opens to all unnamed modules, we can use the ALL-UNNAMED:

java --add-opens java.base/java.lang=ALL-UNNAMED

3.3. At Runtime

To add opens at runtime, we can use the Module.addOpens method:

srcModule.addOpens("com.baeldung.reflected.internal", targetModule);

In the above code snippet, the srcModule opens the “com.baeldung.reflected.internal” package to the targetModule.

One thing to note: the Module.addOpens method is caller-sensitive. This method will succeed only when we call it from the module being modified, from the modules it has granted open access to, or from the unnamed module. Otherwise, it will lead to an IllegalCallerException.

Another way to add opens to the target module is using the Java agent. In the java.instrument module, the Instrumentation class has added a new redefineModule method since Java 9. This method can be used to add extra reads, exports, opens, uses, and provides:

void redefineModule(Instrumentation inst, Module src, Module target) {
    // prepare extra reads
    Set<Module> extraReads = Collections.singleton(target);

    // prepare extra exports
    Set<String> packages = src.getPackages();
    Map<String, Set<Module>> extraExports = new HashMap<>();
    for (String pkg : packages) {
        extraExports.put(pkg, extraReads);
    }

    // prepare extra opens
    Map<String, Set<Module>> extraOpens = new HashMap<>();
    for (String pkg : packages) {
        extraOpens.put(pkg, extraReads);
    }

    // prepare extra uses
    Set<Class<?>> extraUses = Collections.emptySet();

    // prepare extra provides
    Map<Class<?>, List<Class<?>>> extraProvides = Collections.emptyMap();

    // redefine module
    inst.redefineModule(src, extraReads, extraExports, extraOpens, extraUses, extraProvides);
}

In the above code, we first utilize the target module to construct the extraReads, extraExports, and extraOpens variables. Then, we invoke the Instrumentation.redefineModule method. As a result, the src module will be accessible to the target module.

4. Conclusion

In this tutorial, we first introduced the readability and accessibility of the module system. Then, we looked at different illegal reflective access use cases and how relaxed strong encapsulation helps us to migrate from Java 8 to the Java 9 module system. Finally, we provided different approaches to solve the illegal reflective access.

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.

Course – LS – NPI (cat=Java)
announcement - icon

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

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments