Introduce public defineClass variant for SmartClassLoader implementations
Closes gh-26403
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.context.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -74,6 +75,11 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
|
||||
return (clazz.getClassLoader() instanceof ContextOverridingClassLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
|
||||
return defineClass(name, b, 0, b.length, protectionDomain);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ClassLoader to be created for each loaded class.
|
||||
|
||||
@@ -522,26 +522,46 @@ public class ReflectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
// Classic option: protected ClassLoader.defineClass method
|
||||
if (c == null && classLoaderDefineClassMethod != null) {
|
||||
// Direct defineClass attempt on the target Classloader
|
||||
if (c == null) {
|
||||
if (protectionDomain == null) {
|
||||
protectionDomain = PROTECTION_DOMAIN;
|
||||
}
|
||||
Object[] args = new Object[]{className, b, 0, b.length, protectionDomain};
|
||||
|
||||
// Look for publicDefineClass(String name, byte[] b, ProtectionDomain protectionDomain)
|
||||
try {
|
||||
if (!classLoaderDefineClassMethod.isAccessible()) {
|
||||
classLoaderDefineClassMethod.setAccessible(true);
|
||||
}
|
||||
c = (Class) classLoaderDefineClassMethod.invoke(loader, args);
|
||||
Method publicDefineClass = loader.getClass().getMethod(
|
||||
"publicDefineClass", String.class, byte[].class, ProtectionDomain.class);
|
||||
c = (Class) publicDefineClass.invoke(loader, className, b, protectionDomain);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw new CodeGenerationException(ex.getTargetException());
|
||||
if (!(ex.getTargetException() instanceof UnsupportedOperationException)) {
|
||||
throw new CodeGenerationException(ex.getTargetException());
|
||||
}
|
||||
// in case of UnsupportedOperationException, fall through
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Fall through if setAccessible fails with InaccessibleObjectException on JDK 9+
|
||||
// (on the module path and/or with a JVM bootstrapped with --illegal-access=deny)
|
||||
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
|
||||
throw new CodeGenerationException(ex);
|
||||
// publicDefineClass method not available -> fall through
|
||||
}
|
||||
|
||||
// Classic option: protected ClassLoader.defineClass method
|
||||
if (c == null && classLoaderDefineClassMethod != null) {
|
||||
Object[] args = new Object[]{className, b, 0, b.length, protectionDomain};
|
||||
try {
|
||||
if (!classLoaderDefineClassMethod.isAccessible()) {
|
||||
classLoaderDefineClassMethod.setAccessible(true);
|
||||
}
|
||||
c = (Class) classLoaderDefineClassMethod.invoke(loader, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw new CodeGenerationException(ex.getTargetException());
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Fall through if setAccessible fails with InaccessibleObjectException on JDK 9+
|
||||
// (on the module path and/or with a JVM bootstrapped with --illegal-access=deny)
|
||||
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
|
||||
throw new CodeGenerationException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by a reloading-aware ClassLoader
|
||||
* (e.g. a Groovy-based ClassLoader). Detected for example by
|
||||
@@ -34,10 +38,41 @@ public interface SmartClassLoader {
|
||||
* Determine whether the given class is reloadable (in this ClassLoader).
|
||||
* <p>Typically used to check whether the result may be cached (for this
|
||||
* ClassLoader) or whether it should be reobtained every time.
|
||||
* The default implementation always returns {@code false}.
|
||||
* @param clazz the class to check (usually loaded from this ClassLoader)
|
||||
* @return whether the class should be expected to appear in a reloaded
|
||||
* version (with a different {@code Class} object) later on
|
||||
*/
|
||||
boolean isClassReloadable(Class<?> clazz);
|
||||
default boolean isClassReloadable(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a custom class (typically a CGLIB proxy class) in this class loader.
|
||||
* <p>This is a public equivalent of the protected
|
||||
* {@code defineClass(String, byte[], int, int, ProtectionDomain)} method
|
||||
* in {@link ClassLoader} which is traditionally invoked via reflection.
|
||||
* A concrete implementation in a custom class loader should simply delegate
|
||||
* to that protected method in order to make classloader-specific definitions
|
||||
* publicly available without "illegal access" warnings on JDK 9+:
|
||||
* {@code return defineClass(name, b, 0, b.length, protectionDomain)}.
|
||||
* Note that the JDK 9+ {@code Lookup#defineClass} method does not support
|
||||
* a custom target class loader for the new definition; it rather always
|
||||
* defines the class in the same class loader as the lookup's context class.
|
||||
* @param name the name of the class
|
||||
* @param b the bytes defining the class
|
||||
* @param protectionDomain the protection domain for the class, if any
|
||||
* @return the newly created class
|
||||
* @throws LinkageError in case of a bad class definition
|
||||
* @throws SecurityException in case of an invalid definition attempt
|
||||
* @throws UnsupportedOperationException in case of a custom definition attempt
|
||||
* not being possible (thrown by the default implementation in this interface)
|
||||
* @since 5.3.4
|
||||
* @see ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineClass(byte[])
|
||||
*/
|
||||
default Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user