I'm experiencing a problem when running my Spring Boot 3.2 application as a JAR with spring.threads.virtual.enabled=true. Under certain conditions, the following error is triggered:
reactor.core.scheduler.Schedulers : Scheduler worker in group VirtualThreads failed with an uncaught exception
java.lang.NoClassDefFoundError: reactor/core/Exceptions
at reactor.core.scheduler.Schedulers.handleError(Schedulers.java:1180) ~[reactor-core-3.6.0.jar!/:3.6.0]
at reactor.core.scheduler.ThreadPerTaskBoundedElasticScheduler$SchedulerTask.run(ThreadPerTaskBoundedElasticScheduler.java:1018) ~[reactor-core-3.6.0.jar!/:3.6.0]
at java.base/java.lang.VirtualThread.run(VirtualThread.java:309) ~[na:na]
Caused by: java.lang.ClassNotFoundException: reactor.core.Exceptions
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593) ~[na:na]
at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104) ~[spring-cloud-2023-test-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[spring-cloud-2023-test-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
... 3 common frames omitted
Upon debugging, I discovered that the actual class that could not be found is "reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber", even though this class does indeed exist in the classpath. This leads me to suspect a class loading issue.
Comment From: wilkinsona
This may be another symptom of https://github.com/spring-projects/spring-boot/issues/38611. Could you please try 3.2.1-SNAPSHOT, available from https://repo.spring.io/snapshot, and let us know if it helps?
Comment From: honhimW
Okay, I will try the 3.2.1-SNAPSHOT later and let you know the results.
Also, I looked at the issue link you provided, where you mentioned trying to preload using Class#forName. I have already tried this, and even though the required class can be found during debugging, the program still reports the same error when running.
Comment From: honhimW
This may be another symptom of #38611. Could you please try 3.2.1-SNAPSHOT, available from https://repo.spring.io/snapshot, and let us know if it helps? @wilkinsona
Well, it seems that upgrading to 3.2.1-SNAPSHOT doesn’t solve the problem... Actually, I’m not quite sure if this problem is caused by Spring Boot, because it could also be Spring Cloud or Project Reactor. So, I hope to get your help. I have created a sample project here to reproduce the error: https://github.com/honhimW/spring-boot-issues-38719.
Comment From: wilkinsona
Thanks for the sample. It appears to be more complex than needed as service discovery shouldn't be involved in the HTTP request handling. Please strip things down to the bare minimum that's required to reproduce the problem. If Spring Cloud Gateway is required, please hardcode it's routing if possible, rather than relying on service discovery and load balancing.
Comment From: honhimW
@wilkinsona I apologize for providing such a complex sample. However, I’m unable to reproduce the error in a simpler way, such as by hardcoding the route or customizing a service instance provider’s Bean or mocking a service instance’s CacheManager. For a specific instance, please refer to the branch: https://github.com/honhimW/spring-boot-issues-38719/tree/without-discovery.😥
Comment From: honhimW
Additionally, If I set the configuration in my build.gradle as follows, the error will not occur:
bootJar {
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
}
Comment From: honhimW
Well, I've simplified the sample for reproduction, but it still need gateway and loadbalancer.
README.md
Comment From: philwebb
@honhimW I still suspect that this is #38611. I notice from the sample that although you're using 3.2.1-SNAPSHOT in your project, you're still using Spring Boot 3.2.0 for the Spring Boot Gradle plugin. It's the plugin that contributes the classes we think we've fixed.
Could you please try upgrading the plugin version to see if that fixes the issue. You'll probably also need to update settings.gradle with the following:
pluginManagement {
repositories {
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
gradlePluginPortal()
}
}
Comment From: honhimW
@philwebb Great! It indeed was an issue with the plugin version. But may I ask why this problem only occurs in version 3.2.0 when virtual-thread is enabled? I’ve looked at the related code fix records and it seems there is no relevant content.
By the way, regarding this issue, should I close it myself or leave it to you?
Comment From: wilkinsona
Thanks for confirming that this works with 3.2.1-SNAPSHOT. Enabling virtual threads changes the classes that are loaded and when they are loaded. It may also affect if and when threads are interrupted. One or both of these would be enough to trigger the problem that #38611 has fixed.