Web applications currently see different context classloaders depending on the lifecycle.

Most of the time the Thread.currentThread().getContextClassLoader() is an org.eclipse.jetty.ee10.webapp.WebAppClassLoader. For example during jakarta.servlet.ServletContextListener#contextInitialized.

During jakarta.servlet.Servlet#init however, the current context classloader is of type jdk.internal.loader.ClassLoaders.AppClassLoader.

Seeing different classloaders for different lifecycle calls in the same servlet context breaks Mojarra and MyFaces.

This PR fixes this, by explicitly setting the context classloader for the deferred servlet initialization.

Comment From: wilkinsona

Thanks for the PR, @larsgrefer. I agree that the ClassLoader should be consistent when initializing servlets, filters, and listeners. Unfortunately, I don't think we can make things consistent using the approach that's proposed here.

The inconsistency exists in 2.7.x and later. Changes like those proposed here are fine in 2.7.x but do not work in 3.0.x. In 3.0.x the change means that a different ClassLoader is used to load org.eclipse.jetty.servlet.DefaultServlet and the load fails as Jetty prevents the web application class loader from loading server types.

I've opened https://github.com/spring-projects/spring-boot/issues/37649 so that we can continue to investigate the problem and hopefully find a solution.