spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.34k stars 40.72k forks source link

Exclude spring-boot-devtools from AOT processing and native images built with Maven #32853

Open wilkinsona opened 2 years ago

wilkinsona commented 2 years ago

We've had a couple of issues in this area already:

The first was Maven-specific as, when using Gradle, the developmentOnly configuration meant that DevTools wasn't on the classpath when performing AOT processing with Gradle. It was fixed by disabling DevTools during AOT processing. The second was Gradle-specific as developmentOnly dependencies were only the classpath of the native image but they hadn't been processed ahead-of-time so a failure occurred when the native image was started.

With the first fix in place, a Maven-built native image will include DevTools but it'll be disabled. As such, its inclusion is benign other than bloating the image. That bloat is only ~145KB as far as I can tell, but we should eliminate it if we can by removing DevTools from the classpath.

sdeleuze commented 1 year ago

Based on my test with Spring Boot 3.0.1, the behavior I see is that creating a project on https://start.spring.io/ with native and Devtools works with Gradle but is broken with Maven at runtime with this exception: https://gist.github.com/sdeleuze/4b3eda4fedfb98eef820cc8888af9c26

Maybe fixing this issue would fix that error and remove the bloat at the same time?

DuncanCasteleyn commented 1 year ago

I have encountered the same issue with maven when using spring-boot:build-image the native image will work, but when using native:compile running the executable will fail due to devtools even though it was configured as an optional dependency. Removing devtools as dependecy from the pom.xml resolves the problem.

mhalbritter commented 1 year ago

We can exclude the devtools in org.springframework.boot.maven.ProcessAotMojo#getClassPath with this:

        Exclude exclude = new Exclude();
        exclude.setGroupId("org.springframework.boot");
        exclude.setArtifactId("spring-boot-devtools");
        return getClassPath(directories, new ExcludeTestScopeArtifactFilter(), new ExcludeFilter(exclude));

But this only excludes the devtools from the AOT processing phase, not from the inclusion in the native image and it will still fail when the native binary runs.

We don't supply native-image with the classpath, this is done in the native-maven-plugin itself here: https://github.com/graalvm/native-build-tools/blob/c4762a78f9c80b653ff4e3be8f3bb3fc6dd60d90/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java#L323

One can override the whole classpath used for native compilation. We don't do that in our starter-parent, we only point native-image to our classes (application classes and AOT generated classes) and let native image itself figure out the classpath.

I think the way forward would be to add something to native-maven-plugin which enables automatic population of the classpath (which is already implemented) but with the option of excluding dependencies. We could then add the exclusion of the devtools in our starter-parent.

What do you think?

wilkinsona commented 1 year ago

That sounds like a good long-term solution although I wonder if they'd agree to the extra complexity for something that is probably quite unusual. In the shorter term, I think we should consider updating DevToolsEnablementDeducer to disable DevTools in a native image. This will still be useful in the longer term even with the proposed changes to the native Maven plugin if the dependency ends up in the native image anyway, for example because someone isn't using spring-boot-starter-parent.

mhalbritter commented 1 year ago

Projects with devtools included are now working again with Maven. The DevToolsPropertyDefaultsPostProcessor will now back off in a native image. The code is still included in the native image, but it won't run.

The problem with excluding devtools from the classpath when building a native image still stands.

oliveryasuna commented 1 year ago

+1. Also experiencing this issue.

wilkinsona commented 1 year ago

@oliveryasuna what problem are you experiencing? AFAIK, there are no longer any problems with Devtools in a native image as it will be disabled.

sdeleuze commented 1 year ago

Could be https://github.com/spring-projects/spring-boot/issues/35853, this currently breaks Petlinic with Maven when org.springframework.boot:spring-boot-devtools is added.

wilkinsona commented 1 year ago

Thanks, @sdeleuze. I'd forgotten about that one. @oliveryasuna, are you using Spring Boot 3.1.0?

GregaVrbancic commented 1 year ago

Hi @wilkinsona, I am experiencing the same issue using Spring Boot 3.1.0. When spring-boot-devtools are included in pom.xml, the application throws the following exception at startup:


        at org.springframework.core.io.support.SpringFactoriesLoader$FailureHandler.lambda$throwing$0(SpringFactoriesLoader.java:651)
        at org.springframework.core.io.support.SpringFactoriesLoader$FailureHandler.lambda$handleMessage$3(SpringFactoriesLoader.java:675)
        at org.springframework.core.io.support.SpringFactoriesLoader.instantiateFactory(SpringFactoriesLoader.java:231)
        at org.springframework.core.io.support.SpringFactoriesLoader.load(SpringFactoriesLoader.java:206)
        at org.springframework.core.io.support.SpringFactoriesLoader.load(SpringFactoriesLoader.java:160)
        at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:462)
        at org.springframework.boot.SpringApplication.getSpringFactoriesInstances(SpringApplication.java:458)
        at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:274)
        at org.springframework.boot.SpringApplication.<init>(SpringApplication.java:253)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1305)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1294)
        at si.um.feri.isl.dataHub.DataHub.main(DataHub.java:26)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.devtools.restart.RestartScopeInitializer
        at java.base@17.0.5/java.lang.Class.forName(DynamicHub.java:1132)
        at org.springframework.util.ClassUtils.forName(ClassUtils.java:284)
        at org.springframework.core.io.support.SpringFactoriesLoader.instantiateFactory(SpringFactoriesLoader.java:224)
        ... 9 more```

However, when I remove spring-boot-devtools dependency everything works fine.
oliveryasuna commented 1 year ago

My workaround was to make spring-boot-devtools scope provided in my native profile.

wilkinsona commented 1 year ago

Thanks, both. This should bee fixed in tomorrow's 3.1.1 release through #35853.

oliveryasuna commented 1 year ago

@wilkinsona Yes, I am using 3.1.0. AOT includes Devtools.

snicoll commented 3 months ago

To hopefully get the ball rolling again, I've submitted a RFE for the native image Maven plugin, see https://github.com/graalvm/native-build-tools/issues/612