From 0b02a5e073dd01df34e69c304d06fee83eb24dd5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 11 Jul 2023 17:51:55 +0200 Subject: [PATCH] Avoid illegal reflective access in ContextOverridingClassLoader Closes gh-22791 --- .../support/ContextTypeMatchClassLoader.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java b/spring-context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java index 1f17f45719ba..c4e228c3c1e5 100644 --- a/spring-context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java +++ b/spring-context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.commons.logging.LogFactory; + import org.springframework.core.DecoratingClassLoader; import org.springframework.core.OverridingClassLoader; import org.springframework.core.SmartClassLoader; @@ -45,15 +47,26 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart } - private static Method findLoadedClassMethod; + @Nullable + private static final Method findLoadedClassMethod; static { + // Try to enable findLoadedClass optimization which allows us to selectively + // override classes that have not been loaded yet. If not accessible, we will + // always override requested classes, even when the classes have been loaded + // by the parent ClassLoader already and cannot be transformed anymore anyway. + Method method = null; try { - findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); + method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); + ReflectionUtils.makeAccessible(method); } - catch (NoSuchMethodException ex) { - throw new IllegalStateException("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!"); + catch (Throwable ex) { + // Typically a JDK 9+ InaccessibleObjectException... + // Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED + LogFactory.getLog(ContextTypeMatchClassLoader.class).debug( + "ClassLoader.findLoadedClass not accessible -> will always override requested class", ex); } + findLoadedClassMethod = method; } @@ -96,13 +109,14 @@ protected boolean isEligibleForOverriding(String className) { if (isExcluded(className) || ContextTypeMatchClassLoader.this.isExcluded(className)) { return false; } - ReflectionUtils.makeAccessible(findLoadedClassMethod); - ClassLoader parent = getParent(); - while (parent != null) { - if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) { - return false; + if (findLoadedClassMethod != null) { + ClassLoader parent = getParent(); + while (parent != null) { + if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, className) != null) { + return false; + } + parent = parent.getParent(); } - parent = parent.getParent(); } return true; }