Class ChildFirstURLClassLoader
- All Implemented Interfaces:
Closeable, AutoCloseable
org.springframework.boot.loader.*: Launcher, JarLauncher, Archive, LaunchedClassLoader,
the "nested:"/"jar:" URL protocol handlers, etc.) from its own URLs before delegating to its
parent. Every other class name uses standard parent-first delegation.
Used to bootstrap in-process Spring Boot fat-jar services (RESTV3, MCP): the JVM's system
classpath also carries spring-boot-loader.jar (required for the JDK to resolve Spring Boot's
"nested:" URL protocol), so with plain parent-first delegation, Spring Boot's own
Launcher/JarLauncher/Archive classes get defined by the system classloader instead of by this
loader - orphaning any extra jars (e.g. lib/optional/security) added only to this loader's own
URLs, since Launcher.createClassLoader() parents its internal app classloader on
"this.getClass().getClassLoader()", i.e. whichever loader actually defined the Launcher class.
The child-first override is deliberately scoped to just that one package: this loader's own URLs also carry jars (e.g. lib/optional/security's spring-security-*) that RESTV3's fat jar bundles its own copies of internally. Applying child-first broadly would let this loader's copy of those classes win over the fat jar's, and since the two copies aren't necessarily the same build, a class defined by this loader can fail to link against a supertype that only exists in the fat jar's own nested dependencies (observed as NoClassDefFoundError for org.springframework.beans.factory.xml.NamespaceHandler when spring-security-config was resolved from lib/optional/security instead of the fat jar's bundled spring-beans). Standard parent-first delegation is what actually resolves lib/optional/security-only classes (e.g. a customer's own LDAP UserDetailsContextMapper) correctly: they simply aren't present on the parent chain above this loader, so delegation falls through to this loader's own URLs regardless.
org.springframework.web.servlet.* is handled the opposite way: delegation to the
parent is refused outright, without even trying this loader's own (equally empty, for this
package) URLs first. The distribution ships a standalone lib/optional/spring/spring-webmvc-*.jar
- reachable from this loader's parent chain - for unrelated Processing-Unit/OpenSpaces Spring
MVC support (see xap-dist's xap-openspaces.xml assembly and
GsCommandFactory.appendSpringClassPath(), used by spaceInstance()/standalonePuInstance() command
building), a real jar with real classes, not a missing one - so plain parent-first delegation
does not fail-and-fall-through the way it does for e.g. org.springdoc.* (which only ever exists
inside RESTV3's own fat jar): the parent chain's copy of e.g.
org.springframework.web.servlet.resource.LiteWebJarsResourceResolver gets defined successfully,
just from the wrong (external, PU-oriented) build. Its own "new WebJarVersionLocator()" then
resolves via *its own* defining classloader - the external one, which has no visibility into
RESTV3's fat jar's nested BOOT-INF/lib - so a class that is genuinely bundled
(webjars-locator-lite) throws NoClassDefFoundError anyway. Refusing to ask the parent for
org.springframework.web.servlet.* at all forces the loader that asked (the Spring Boot
LaunchedURLClassLoader nested inside RESTV3's own fat jar, a child of this loader) to fall back
to its own bundled copy instead, the same copy the rest of the fat jar's Spring context is
already using.
The prefix is exactly org.springframework.web.servlet. - the entire content of the
spring-webmvc jar, no more and no less (verified: spring-web-*.jar has zero classes under
org/springframework/web/servlet/, and spring-webmvc-*.jar has nothing else) - not narrower and
not broader. Two earlier, wrong boundaries were each fixed by a regression at the next one:
- Blocking all of org.springframework.web.* (too broad) additionally caught
org.springframework.web.util.pattern.PathPatternParser and org.springframework.web.filter.
GenericFilterBean - both spring-web classes, not spring-webmvc. spring-security-web
(lib/optional/security, kept external on this loader's own parent chain deliberately for LDAP
customization - see above; never a descendant of this loader, so blocking a package here has no
effect on its own resolution) needs those from the *same* external realm it itself loads from,
for XML
<http>filter-chain parsing and pathPatternRequestMatcherBuilder respectively. Forcing them onto the fat jar's copy instead just moved a two-copies conflict from NoClassDefFoundError (on webjars) to BeanNotOfRequiredTypeException / BeanDefinitionParsingException. - Narrowing all the way down to just org.springframework.web.servlet.resource.* (too narrow) fixed that, but then missed that other org.springframework.web.servlet.* classes still resolved externally too - e.g. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport, which itself creates a org.springframework.web.servlet.resource.ResourceUrlProvider bean using *its own* (external) defining classloader, never asking this loader at all since WebMvcConfigurationSupport itself was never blocked. Meanwhile org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration (fat-jar-only, spring-boot- autoconfigure) expects a ResourceUrlProvider parameter type resolved *through* this loader - forced to the fat jar's copy by the block. Two distinct Class objects again, this time within the blocked package itself: NoSuchBeanDefinitionException on ResourceUrlProvider building 'welcomePageHandlerMapping' (observed via jay.dalal's ci-5603 repro).
-
Constructor Summary
Constructors -
Method Summary
Methods inherited from class URLClassLoader
addURL, close, definePackage, findResource, findResources, getPermissions, getResourceAsStream, getURLs, newInstance, newInstanceMethods inherited from class SecureClassLoader
defineClass, defineClassMethods inherited from class ClassLoader
clearAssertionStatus, defineClass, defineClass, defineClass, defineClass, definePackage, findClass, findLibrary, findLoadedClass, findResource, findSystemClass, getClassLoadingLock, getDefinedPackage, getDefinedPackages, getName, getPackage, getPackages, getParent, getPlatformClassLoader, getResource, getResources, getSystemClassLoader, getSystemResource, getSystemResourceAsStream, getSystemResources, getUnnamedModule, isRegisteredAsParallelCapable, loadClass, registerAsParallelCapable, resolveClass, resources, setClassAssertionStatus, setDefaultAssertionStatus, setPackageAssertionStatus, setSigners
-
Constructor Details
-
ChildFirstURLClassLoader
-
-
Method Details
-
loadClass
- Overrides:
loadClassin classClassLoader- Throws:
ClassNotFoundException
-
findClass
- Overrides:
findClassin classURLClassLoader- Throws:
ClassNotFoundException
-