spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Declare Spring modules with JDK 9 module metadata [SPR-13501]

Open spring-projects-issues opened this issue 8 years ago • 28 comments

Juergen Hoeller opened SPR-13501 and commented

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.


Issue Links:

  • #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

spring-projects-issues avatar Sep 24 '15 18:09 spring-projects-issues

Sam Brannen commented

Juergen Hoeller, is this issue a duplicate of #18289?

spring-projects-issues avatar Jun 15 '16 14:06 spring-projects-issues

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

spring-projects-issues avatar Jun 22 '16 20:06 spring-projects-issues

Sam Brannen commented

OK. I see the differences between the two issues now. Thanks for the clarification!

spring-projects-issues avatar Jun 22 '16 20:06 spring-projects-issues

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

spring-projects-issues avatar Jun 28 '16 22:06 spring-projects-issues

Sam Brannen commented

while still keeping them compatible with JDK 9

Is that supposed to say "JDK 8"?

spring-projects-issues avatar Apr 17 '17 14:04 spring-projects-issues

Grigory Kislin commented

Hello! 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.

spring-projects-issues avatar Sep 05 '18 16:09 spring-projects-issues

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.

spring-projects-issues avatar Sep 12 '18 09:09 spring-projects-issues

yboompook commented

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.

poc on github

stackoverflow

spring-projects-issues avatar Nov 30 '18 11:11 spring-projects-issues

Any progress with this issue?

jabrena avatar Dec 19 '20 17:12 jabrena

Sounds like this will happen maybe 10-15 years from now and by then we'll be on JDK 50.

cesarbiods avatar Jan 05 '21 14:01 cesarbiods

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.

xenoterracide avatar Jun 23 '21 15:06 xenoterracide

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

My SO question on this topic.

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.

lpandzic avatar Sep 24 '21 13:09 lpandzic

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.

dsyer avatar Sep 28 '21 09:09 dsyer

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:

  1. put whole hierarchy inside one package
  2. 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.

lpandzic avatar Sep 28 '21 11:09 lpandzic

Now that Spring Framework 6 is on the horizon, have there been any thoughts towards moving to modules?

Sineaggi avatar May 07 '22 02:05 Sineaggi

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.

jhoeller avatar Oct 05 '22 15:10 jhoeller

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.

Edit (2023-04-24): I misunderstood the opens clause in JPMS: it is only required if you want the reflecting code to break encapsulation (setAccessible etc.). If you do proper constructor injection and only serialize/deserialize via public constructors, you wouldn't even need to open your implementations to Spring, only export them. This reduces the attack surface for reflection gadgets. Opens would probably be required for runtime AOT proxies though, as they have to be created inside the target package. Also note that even if you open just a single package, by defining a class in that package you gain reflective access to the entire module. Users of a modularized Spring would have to be aware of this, and Spring would have to be very cautious about the rights of runtime generated classes.

grubeninspekteur avatar Nov 11 '22 08:11 grubeninspekteur

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.

xenoterracide avatar Nov 11 '22 11:11 xenoterracide

Has anyone done any benchmarks on startup time with large codebases? I don't fully understand the low level implications of JPMS. Isn't it supposed to know what jar's have what packages, and so if you're doing classpath scanning it wouldn't open a jar at all that doesn't allow you to opens (or whatever, my jpms is rusty)? My mental theory is that in a moderate+ sized codebase, jpms across the board for hibernate and spring could speedup startup time. Sadly I'm not working on such a project atm, but if someone has one and it's gradle they could use this to test maybe.

https://github.com/gradlex-org/extra-java-module-info

xenoterracide avatar Jan 25 '24 16:01 xenoterracide