This is the scenario:
- A web application uses a library.
- That library needs to dynamically load a class that implements a specific interface, for example, in order to select the best implementation for the specific environment in which it is being run.
When using Spring Web Reactive in a Spring Boot 2.0.0 (snapshot) application, if Tomcat is used as a server, a ClassNotFound exception is thrown when the library tries to load the implementation class by means of the thread context class loader.
Example application: https://github.com/danielfernandez/test-spring-boot-tomcat
The above application replicates the scenario with Spring Boot 1.4.2 and 2.0.0, using Spring Web MVC and Spring Web Reactive (in 2.0.0). Spring Web Reactive is tested both using Netty and Tomcat. Only Tomcat fails.
This is possibly due to the hierarchy of the thread context class loader. Let's compare:
Thread context class loader in Spring Boot 2.0.0, Spring Web MVC 5.0.0, Tomcat:
Thread context class loader in Spring Boot 2.0.0, Spring Web Reactive 5.0.0, Netty:
Thread context class loader in Spring Boot 2.0.0, Spring Web Reactive 5.0.0, Tomcat:
Given the org.springframework.boot.loader.LaunchedURLClassLoader is the one which class path contains all the .jar files contained inside the Spring Boot über jar, it seems as if the org.apache.catalina.loader.ParallelWebappClassLoader used as a thread context class loader in Spring Web Reactive + Tomcat should be delegating to LaunchedURLClassLoader. But it isn't.
Please see the README for the example application on GitHub for more detail on how this was tested and diagnosed.
This affects the integration of Thymeleaf into applications using Spring Web Reactive. Thymeleaf needs to initialise some implementations depending on the specific scenario it is being run on, and this issue makes it impossible to use the current version of the Thymeleaf + Spring integration packages in Spring Web Reactive applications using Tomcat.