Align with SmartClassLoader handling for AOP proxy classes
Closes gh-34274
This commit is contained in:
parent
124b38450b
commit
f53da04717
|
@ -109,8 +109,16 @@ class ConfigurationClassEnhancer {
|
||||||
}
|
}
|
||||||
return configClass;
|
return configClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
|
// Use original ClassLoader if config class not locally loaded in overriding class loader
|
||||||
|
if (classLoader instanceof SmartClassLoader smartClassLoader &&
|
||||||
|
classLoader != configClass.getClassLoader()) {
|
||||||
|
classLoader = smartClassLoader.getOriginalClassLoader();
|
||||||
|
}
|
||||||
|
Enhancer enhancer = newEnhancer(configClass, classLoader);
|
||||||
|
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
|
||||||
|
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
|
||||||
configClass.getName(), enhancedClass.getName()));
|
configClass.getName(), enhancedClass.getName()));
|
||||||
|
@ -155,8 +163,21 @@ class ConfigurationClassEnhancer {
|
||||||
* Uses enhancer to generate a subclass of superclass,
|
* Uses enhancer to generate a subclass of superclass,
|
||||||
* ensuring that callbacks are registered for the new subclass.
|
* ensuring that callbacks are registered for the new subclass.
|
||||||
*/
|
*/
|
||||||
private Class<?> createClass(Enhancer enhancer) {
|
private Class<?> createClass(Enhancer enhancer, boolean fallback) {
|
||||||
Class<?> subclass = enhancer.createClass();
|
Class<?> subclass;
|
||||||
|
try {
|
||||||
|
subclass = enhancer.createClass();
|
||||||
|
}
|
||||||
|
catch (CodeGenerationException ex) {
|
||||||
|
if (!fallback) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
// Possibly a package-visible @Bean method declaration not accessible
|
||||||
|
// in the given ClassLoader -> retry with original ClassLoader
|
||||||
|
enhancer.setClassLoader(null);
|
||||||
|
subclass = enhancer.createClass();
|
||||||
|
}
|
||||||
|
|
||||||
// Registering callbacks statically (as opposed to thread-local)
|
// Registering callbacks statically (as opposed to thread-local)
|
||||||
// is critical for usage in an OSGi environment (SPR-5932)...
|
// is critical for usage in an OSGi environment (SPR-5932)...
|
||||||
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
|
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2024 the original author or authors.
|
* Copyright 2002-2025 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,11 +18,16 @@ package org.springframework.context.annotation;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
import java.security.SecureClassLoader;
|
import java.security.SecureClassLoader;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.OverridingClassLoader;
|
||||||
import org.springframework.core.SmartClassLoader;
|
import org.springframework.core.SmartClassLoader;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -36,19 +41,108 @@ class ConfigurationClassEnhancerTests {
|
||||||
@Test
|
@Test
|
||||||
void enhanceReloadedClass() throws Exception {
|
void enhanceReloadedClass() throws Exception {
|
||||||
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||||
|
|
||||||
ClassLoader parentClassLoader = getClass().getClassLoader();
|
ClassLoader parentClassLoader = getClass().getClassLoader();
|
||||||
CustomClassLoader classLoader = new CustomClassLoader(parentClassLoader);
|
ClassLoader classLoader = new CustomSmartClassLoader(parentClassLoader);
|
||||||
Class<?> myClass = parentClassLoader.loadClass(MyConfig.class.getName());
|
Class<?> myClass = parentClassLoader.loadClass(MyConfig.class.getName());
|
||||||
configurationClassEnhancer.enhance(myClass, parentClassLoader);
|
Class<?> enhancedClass = configurationClassEnhancer.enhance(myClass, parentClassLoader);
|
||||||
Class<?> myReloadedClass = classLoader.loadClass(MyConfig.class.getName());
|
assertThat(myClass).isAssignableFrom(enhancedClass);
|
||||||
Class<?> enhancedReloadedClass = configurationClassEnhancer.enhance(myReloadedClass, classLoader);
|
|
||||||
assertThat(enhancedReloadedClass.getClassLoader()).isEqualTo(classLoader);
|
myClass = classLoader.loadClass(MyConfig.class.getName());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(myClass, classLoader);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
|
||||||
|
assertThat(myClass).isAssignableFrom(enhancedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withPublicClass() {
|
||||||
|
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||||
|
|
||||||
|
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
|
||||||
|
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
|
||||||
|
|
||||||
|
classLoader = new OverridingClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withNonPublicClass() {
|
||||||
|
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||||
|
|
||||||
|
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
|
||||||
|
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new OverridingClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withNonPublicMethod() {
|
||||||
|
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||||
|
|
||||||
|
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
|
||||||
|
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
|
||||||
|
|
||||||
|
classLoader = new OverridingClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
|
|
||||||
|
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
|
||||||
|
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
|
||||||
|
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
|
||||||
|
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
static class MyConfig {
|
static class MyConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
String myBean() {
|
||||||
|
return "bean";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public static class MyConfigWithPublicClass {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public String myBean() {
|
public String myBean() {
|
||||||
return "bean";
|
return "bean";
|
||||||
|
@ -56,9 +150,29 @@ class ConfigurationClassEnhancerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static class CustomClassLoader extends SecureClassLoader implements SmartClassLoader {
|
@Configuration
|
||||||
|
static class MyConfigWithNonPublicClass {
|
||||||
|
|
||||||
CustomClassLoader(ClassLoader parent) {
|
@Bean
|
||||||
|
public String myBean() {
|
||||||
|
return "bean";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public static class MyConfigWithNonPublicMethod {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
String myBean() {
|
||||||
|
return "bean";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class CustomSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
|
||||||
|
|
||||||
|
CustomSmartClassLoader(ClassLoader parent) {
|
||||||
super(parent);
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +196,29 @@ class ConfigurationClassEnhancerTests {
|
||||||
public boolean isClassReloadable(Class<?> clazz) {
|
public boolean isClassReloadable(Class<?> clazz) {
|
||||||
return clazz.getName().contains("MyConfig");
|
return clazz.getName().contains("MyConfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getOriginalClassLoader() {
|
||||||
|
return getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
|
||||||
|
return defineClass(name, b, 0, b.length, protectionDomain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class BasicSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
|
||||||
|
|
||||||
|
BasicSmartClassLoader(ClassLoader parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
|
||||||
|
return defineClass(name, b, 0, b.length, protectionDomain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue