11/*
2- * Copyright 2002-2021 the original author or authors.
2+ * Copyright 2002-2023 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
2121import java .util .Map ;
2222import java .util .concurrent .ConcurrentHashMap ;
2323
24+ import org .apache .commons .logging .LogFactory ;
25+
2426import org .springframework .core .DecoratingClassLoader ;
2527import org .springframework .core .OverridingClassLoader ;
2628import org .springframework .core .SmartClassLoader ;
@@ -45,15 +47,26 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
4547 }
4648
4749
48- private static Method findLoadedClassMethod ;
50+ @ Nullable
51+ private static final Method findLoadedClassMethod ;
4952
5053 static {
54+ // Try to enable findLoadedClass optimization which allows us to selectively
55+ // override classes that have not been loaded yet. If not accessible, we will
56+ // always override requested classes, even when the classes have been loaded
57+ // by the parent ClassLoader already and cannot be transformed anymore anyway.
58+ Method method = null ;
5159 try {
52- findLoadedClassMethod = ClassLoader .class .getDeclaredMethod ("findLoadedClass" , String .class );
60+ method = ClassLoader .class .getDeclaredMethod ("findLoadedClass" , String .class );
61+ ReflectionUtils .makeAccessible (method );
5362 }
54- catch (NoSuchMethodException ex ) {
55- throw new IllegalStateException ("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!" );
63+ catch (Throwable ex ) {
64+ // Typically a JDK 9+ InaccessibleObjectException...
65+ // Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
66+ LogFactory .getLog (ContextTypeMatchClassLoader .class ).debug (
67+ "ClassLoader.findLoadedClass not accessible -> will always override requested class" , ex );
5668 }
69+ findLoadedClassMethod = method ;
5770 }
5871
5972
@@ -96,13 +109,14 @@ protected boolean isEligibleForOverriding(String className) {
96109 if (isExcluded (className ) || ContextTypeMatchClassLoader .this .isExcluded (className )) {
97110 return false ;
98111 }
99- ReflectionUtils .makeAccessible (findLoadedClassMethod );
100- ClassLoader parent = getParent ();
101- while (parent != null ) {
102- if (ReflectionUtils .invokeMethod (findLoadedClassMethod , parent , className ) != null ) {
103- return false ;
112+ if (findLoadedClassMethod != null ) {
113+ ClassLoader parent = getParent ();
114+ while (parent != null ) {
115+ if (ReflectionUtils .invokeMethod (findLoadedClassMethod , parent , className ) != null ) {
116+ return false ;
117+ }
118+ parent = parent .getParent ();
104119 }
105- parent = parent .getParent ();
106120 }
107121 return true ;
108122 }
0 commit comments