A frequent misconception when adopting the Java Platform Module System (JPMS) is that frameworks like Spring fully support modularization out-of-the-box. Developers often run into errors such as “Unable to access class via reflection” or “package is not visible”. This is because Spring relies heavily on reflection, which JPMS restricts by default.
Understanding how to properly integrate Spring with JPMS is essential in real-world applications. Large enterprise systems and microservices often combine Spring Boot with JPMS to enforce encapsulation while still allowing reflective access for dependency injection, proxies, and runtime enhancements.
Think of Spring in a modular world as an auditor who inspects office files. By default, JPMS keeps most files locked. For Spring to function, you need to provide controlled access keys (opens) so it can continue inspecting and wiring beans.
Key Considerations for Spring and JPMS
1. Reflection in Spring
Spring uses reflection for:
- Dependency injection
- Bean instantiation
- Proxy generation
- AOP (Aspect-Oriented Programming)
Without opens, Spring cannot reflectively access internal classes.
2. Using opens in module-info.java
Instead of open (which opens the entire module), prefer targeted opens.
module com.example.app {
requires spring.context;
requires spring.beans;
opens com.example.app.service to spring.core, spring.beans, spring.context;
}
Example: Spring Boot Application with JPMS
module-info.java
module com.example.springbootapp {
requires spring.boot;
requires spring.boot.autoconfigure;
requires spring.context;
requires spring.web;
opens com.example.springbootapp.controller to spring.core, spring.beans, spring.web;
opens com.example.springbootapp.service to spring.core, spring.beans, spring.context;
}
Key points:
- Only necessary packages are opened
opensallows Spring to use reflection safely- Core APIs remain encapsulated
Best Practices & Pitfalls
✅ Best Practices
- Use
opensper package instead ofopenmodule-wide - Keep
module-info.javaminimal, declaring only necessary dependencies - Combine JPMS with
jlinkfor smaller Spring Boot runtimes - Use dedicated test modules with
opensfor JUnit/TestNG
❌ Pitfalls
- Using
open module ...(unnecessary exposure) - Exporting internal packages for Spring instead of
opens - Forgetting reflective access for controllers or beans → runtime errors
- Relying solely on automatic modules for Spring dependencies
Real-World Integration Steps
-
Start Small
Modularize core business logic modules before Spring Boot entry points. -
Handle Reflection Carefully
Use targetedopensfor controllers, services, and entities. -
Custom Runtime with jlink
Build optimized Spring Boot runtimes by including only required JDK modules. -
Testing
Add--add-opensin Maven/Gradle test configs for reflective test frameworks.
What's New in Java Versions?
- Java 5–8 → N/A (no JPMS)
- Java 9 → JPMS introduced, Spring initially struggled with reflection restrictions
- Java 11 → Spring Boot and Spring Framework improved JPMS compatibility
- Java 17 → Stable support with clearer integration patterns
- Java 21 → No significant updates across Java versions for this feature
Real-World Analogy
Integrating Spring with JPMS is like granting a trusted auditor temporary access to inspect certain rooms in a secure office building. You don’t hand them the master key (open), but instead give them access only to relevant departments (opens).
Summary & Key Takeaways
- Spring relies on reflection, which JPMS restricts by default
- Use targeted
opensinmodule-info.javafor Spring packages - Avoid exposing entire modules unnecessarily
- Combine JPMS with Spring Boot for secure, maintainable applications
- Use tooling (Maven/Gradle +
--add-opens) to configure tests properly
FAQ: Spring and Java Modules
1. What is the difference between classpath and module path for Spring apps?
Classpath exposes everything; module path enforces boundaries, requiring explicit opens.
2. Why do I get “package is not visible” errors in Spring Boot apps?
Because the required package isn’t opens to Spring.
3. What’s the purpose of requires transitive in modular Spring apps?
It allows downstream modules to inherit Spring dependencies automatically.
4. How do open and opens differ for Spring integration?open exposes the entire module; opens grants reflection only on specified packages.
5. Do automatic modules work for Spring Boot dependencies?
Yes, but they’re a migration aid, not recommended long-term.
6. How does JPMS improve security in Spring applications?
By enforcing encapsulation and minimizing reflection to trusted packages.
7. When should I use jlink vs jmod with Spring?
Use jmod for packaging dependencies, jlink for optimized runtime images.
8. Can I modularize a Spring Boot app incrementally?
Yes, start with business logic modules and gradually modularize entry points.
9. How do I handle third-party libraries not modularized?
Place them on the classpath or use automatic modules temporarily.
10. Is Spring fully JPMS-compliant today?
Yes, modern Spring versions work with JPMS, though configuration is required for reflection-heavy features.