Disclaimer: I am aware that fully supporting the module system is a non-goal of Spring Boot and the Spring framework (or was, last I checked). This is merely hoping that a fix exists/is simple. If need be I'd be glad to have a go at it myself, alas my current understanding is a bit too limited at the moment, and all I know of the cause of the issue really boils down to a few shaky hypothesis.

Hello,

After a bit of fiddling, I got a Spring Boot app to run properly on the module-path via the spring-boot:run maven goal. (why even try using JPMS? long story short, because JavaFX sort-of needs it).

Split packages, required modules, dynamic proxies etc work fine, but one issue is left, some classpath resource issues with Spring when the application is not ran using that maven goal (i.e. when started like any other Java app).

Versions where I have had the issue (although it's not very relevant afaik)

  • Java 10, 11, 12
  • Maven 3.6.0 (3.5.... as well)
  • Spring Boot 2.1.2 (no particular overrides, so basically Spring 5.1.4 under it)
  • No specific OS (same on the main 2 and a few linux distros)

Issue :

Running the main class "normally" instead of via the maven goal aforementioned yields various properties being undefined crashes with:

Error creating ... 'entityManagerFactory' ... Invocation of init method failed; ... Unable to resolve persistence unit root URL ... class path resource [] cannot be resolved to URL because it does not exist

The cause is :

The defaultPersistenceUnitManager looks up "classpath:" as a resource during initialization, and then this is transformed to a URL via the ClassPathResource class, entering

protected URL resolveURL() {
    if (this.clazz != null) {
        return this.clazz.getResource(this.path);
    }
    else if (this.classLoader != null) {
        return this.classLoader.getResource(this.path); <-- issue here
    }
    else {
        return ClassLoader.getSystemResource(this.path);
    }
}

and the difference is how the second branch of the if works between directly running or running via maven.

Via maven goal : this.classLoader.getResource(this.path); evaluates to path/to/project/target/classes

Without maven goal : this.classLoader.getResource(this.path); evaluates to null, which in turn makes the ClassPathResource#exists method return false and apparently generates confusion in the autoconfiguration.

That said, this is not only an auto-configuration issue as excluding this one (the Hibernate one) yields other issues that indicate that application.properties is not being read properly, or at least that keys defined in .properties files do not end up in the Environment as they should.

My current workaround :

The "trick", which gives me hope the solution is not too out of this world, which is using the spring-boot:run maven plugin+task.

I believe it might be working because it overrides the classloader of the main thread in RunMojo#L94 ?

This issue is either a question (sorry) if the fix is on the client side, or basically asking for guidance on how to go about it. Also I know this looks like it might be Spring Boot / Autoconfiguration related, but really it is about the ClassUtils#getDefaultClassLoader class loader behaving apparently slightly differently when in a module path, yet the Spring Boot plugin "fixing" it somehow (as if it just disabled modules my app would not start due to JavaFX 11 not really supporting the classpath anymore)

I will try to provide a sample project to demonstrate the issue asap. In the meantime, you can check it directly here : https://github.com/Tristan971/Lyrebird (clean install whole project and then start the lyrebird module's application class both via mvn spring-boot:run and by running the Lyrebird class.

Thank you for the (long, sorry again) read.

Comment From: snicoll

Thanks for the report and the description, and sorry this got overlooked but we can't really discuss a potential change with the Spring Boot maven plugin here. Generally speaking you are right about the non-goals of running on the module path.