spring-framework copied to clipboard
Declare Spring modules with JDK 9 module metadata [SPR-13501]
JDK 9's Jigsaw initiative aims to allow for module metadata (
module-info.java) that can be added to framework and library jars while still keeping them compatible with JDK 8. Let's do this for Spring Framework 5.0's modules as far as feasible. However, we might not be able to express our optional dependency arrangement that way, in which case we might have to resort to an "automatic modules" approach for the more modest purposes of #18289.
- #18289 Stable module names for Spring Framework jars on JDK 9 module path ("depends on")
- #17778 Upgrade core framework build to JDK 11 ("depends on")
- #19148 Document Spring recommendations/restrictions for Java 9 module setups
1 votes, 12 watchers
Juergen Hoeller commented
Not necessarily: This one is about shipping explicit
module-info.java files in our own jars, and is rather unlikely to happen at this point since we cannot express optional dependencies that way. However, for the purposes of #18289, we could also aim for what Jigsaw calls "automatic modules", i.e. putting our regular jars onto the module path and letting them participate in a module-based arrangement that way (with implicit access to all other modules).
Sam Brannen commented
OK. I see the differences between the two issues now. Thanks for the clarification!
Juergen Hoeller commented
The Jigsaw page has just been updated with a proposal towards optional dependencies: http://openjdk.java.net/projects/jigsaw/spec/issues/#CompileTimeDependences
Sam Brannen commented
while still keeping them compatible with JDK 9
Is that supposed to say "JDK 8"?
Grigory Kislin commented
Are module-info.java planned to add in Spring 5.1, as was announced in video [Spring Framework 5.0 on JDK 8 & 9|https://www.youtube.com/watch?v=0-sbPBf81KA]
? In recent 5.1.RC2 version they are missed.
Juergen Hoeller commented
This issue is marked as "General Backlog", indicating that we won't deal with it for 5.1 (otherwise it'd be marked for 5.1 GA still) and probably not in subsequent 5.x releases either (otherwise it'd be marked as "5.x Backlog").
Specifically, we can't ship
module-info files quite yet since we'd need stable module names for all of our optional dependencies... and many of those don't declare stable module names at this point (that is, they don't even include an
Automatic-Module-Name manifest entry in their jar). Also, we'd need to build the entire framework on JDK 9+ for the compiler to understand the
module-info.java format which is not entirely trivial either, even if the framework itself is known to work fine with JDK 9/10/11 at runtime.
All in all, my prediction about
module-info files for 5.1 turned out to be too ambitious. Our current focus is on general JDK 11 compatibility (#20937) on the classpath and as automatic modules on the module path, as well as GraalVM compatibility (#21529). The use of
jlink requires manual addition of
module-info.class files to the framework jars for the time being... which might stay that way for several years still until we ship a JDK 11 baselined Spring Framework 6.0 against a new generation of dependencies.
I tried to jlink my project with moditect (maven plugin to add module-info to dependencies that don't have one), java-11. I pass throw lot's of problem and it still doen't work, however I think my work could help you to make it work in spring 6.
Any progress with this issue?
Sounds like this will happen maybe 10-15 years from now and by then we'll be on JDK 50.
also checking in, is there anything that other people can help with on this? as I too would like to see more done than simply automatic module naming.
It seems that new Java features might be more feature rich when using module system with one example being JEP 409: Sealed classes:
The classes specified by permits must be located near the superclass: either in the same module (if the superclass is in a named module) or in the same package (if the superclass is in the unnamed module).
While JEP 409 most likely won't be used much until JEP 406: Pattern Matching for switch comes out of preview, I believe this sets a clear precedent of where we are going.
For the sake of clarity, you can build a
jlink package for a Spring Boot app. It works well with Java 11 and 17 (with 17 much leaner at runtime), but only includes JDK modules for reasons explained above - the other dependencies have to be added on the classpath. Notes here: https://github.com/dsyer/sample-docker-microservice.
Just to reemphasize my previous comment: this is first example, that I know of, of a Java language feature being semi locked behind modularization.
Split packages are biggest obstacle to modularization and split packages are everywhere.
Regarding the Sealed Classes and Pattern matching I believe the pragmatic choice today is to either:
- put whole hierarchy inside one package
- put whole hierarchy in a separate Maven module and then Java modularize just that Maven module
I was surprised that things like Lombok work well with modularization with some configuration changes.
The clean way forward is to modularize the whole application but the ecosystem is simply not ready for this and unless a major player like Spring or Jakarta or someone else pushes "small" library maintainers to modularise their code I don't see modularization happening any time soon - it's simply too big of an undertaking for every developer to isolate/repackage every dependency that breaks modularization rules.
Now that Spring Framework 6 is on the horizon, have there been any thoughts towards moving to modules?
Our strategic alignment with the module system has been in competition with our AOT and GraalVM native image efforts in 6.0, so we unfortunately had no chance to experiment with a build migration to full module descriptors yet. There have been very few requests for it even in the course of this year, so we wonder whether there is much practical value to be uncovered here for the time being anyway. Looking forward, the use of jlink's module-bounded approach for application/framework-level modules might get superseded by runtime images based on GraalVM-style individual reachability analysis in the long run.
That said, OpenJDK's Project Leyden aims to reuse module system concepts and tools for its standardized static image approach, so deeper module system alignment remains part of our long-term technology strategy for the Spring Framework 6.x generation.
Just adding some thoughts for your consideration of modules in the long term: Smaller artifacts generated by jlink are not the only benefit (cf. Ron Pressler: What modules are about). One is having an encapsulation level above package. For example, classes like Function in the
org.springframework.cglib.core.internal package can be properly hidden from user code and only exported to Spring's own modules. This also allows to evolve classes like
Assert without having to deprecate methods first. People tend to import these types by accident, despite the "internal" warning. As a bonus, the number of code completion suggestions for such types in IDEs is reduced.
Directly following from the encapsulation feature is more secure code, since modules must explicitly open their types for reflection. Now of course Spring relies heavily on reflection, so you would have to open most of your beans/data types to it anyway; but you could restrict access only to those parts that need it; for example, open beans to spring.core, but not spring.validation, so a vulnerability in the validation module could not be exploited to gain access to a private EntityManager, for example. Native images somewhat mitigate reflection attacks with their requirement of explicitly stating the types to reflect on, but they do not discriminate between who is doing the reflection.
However, since adding such strong encapsulation would be a breaking change, I do not expect it to arrive before the next major bump of Spring. And like you said, there are no stable automatic module names in all dependencies. Unfortunately, unlike generics, we seem to have arrived at a catch-22 situation with JPMS. No one bothers to use it until the big libraries and frameworks migrate (often taking the role of trailblazers, see AOT), and they won't migrate since there is little demand.
My personal reason for wanting this is to avoid accidental imports as well. Unfortunately tools like Gradle and maven only go so far to prevent you from importing a jar that you didn't intentionally intend to import from. Basically trying to avoid transient hell. I've seen people not only import from internal packages but also from utility packages of libraries that we'd rather not be using directly in that way, for example.