Skip to content

Commit 24fd094

Browse files
committed
Align with SmartClassLoader handling for AOP proxy classes
Closes gh-34274 (cherry picked from commit f53da04)
1 parent 5b928f4 commit 24fd094

File tree

2 files changed

+169
-11
lines changed

2 files changed

+169
-11
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,16 @@ public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader)
109109
}
110110
return configClass;
111111
}
112+
112113
try {
113-
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
114+
// Use original ClassLoader if config class not locally loaded in overriding class loader
115+
if (classLoader instanceof SmartClassLoader smartClassLoader &&
116+
classLoader != configClass.getClassLoader()) {
117+
classLoader = smartClassLoader.getOriginalClassLoader();
118+
}
119+
Enhancer enhancer = newEnhancer(configClass, classLoader);
120+
boolean classLoaderMismatch = (classLoader != null && classLoader != configClass.getClassLoader());
121+
Class<?> enhancedClass = createClass(enhancer, classLoaderMismatch);
114122
if (logger.isTraceEnabled()) {
115123
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
116124
configClass.getName(), enhancedClass.getName()));
@@ -155,8 +163,21 @@ private boolean isClassReloadable(Class<?> configSuperClass, @Nullable ClassLoad
155163
* Uses enhancer to generate a subclass of superclass,
156164
* ensuring that callbacks are registered for the new subclass.
157165
*/
158-
private Class<?> createClass(Enhancer enhancer) {
159-
Class<?> subclass = enhancer.createClass();
166+
private Class<?> createClass(Enhancer enhancer, boolean fallback) {
167+
Class<?> subclass;
168+
try {
169+
subclass = enhancer.createClass();
170+
}
171+
catch (CodeGenerationException ex) {
172+
if (!fallback) {
173+
throw ex;
174+
}
175+
// Possibly a package-visible @Bean method declaration not accessible
176+
// in the given ClassLoader -> retry with original ClassLoader
177+
enhancer.setClassLoader(null);
178+
subclass = enhancer.createClass();
179+
}
180+
160181
// Registering callbacks statically (as opposed to thread-local)
161182
// is critical for usage in an OSGi environment (SPR-5932)...
162183
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassEnhancerTests.java

+145-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 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.
@@ -18,11 +18,16 @@
1818

1919
import java.io.IOException;
2020
import java.io.InputStream;
21+
import java.net.URL;
22+
import java.net.URLClassLoader;
23+
import java.security.ProtectionDomain;
2124
import java.security.SecureClassLoader;
2225

2326
import org.junit.jupiter.api.Test;
2427

28+
import org.springframework.core.OverridingClassLoader;
2529
import org.springframework.core.SmartClassLoader;
30+
import org.springframework.lang.Nullable;
2631
import org.springframework.util.StreamUtils;
2732

2833
import static org.assertj.core.api.Assertions.assertThat;
@@ -36,29 +41,138 @@ class ConfigurationClassEnhancerTests {
3641
@Test
3742
void enhanceReloadedClass() throws Exception {
3843
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
44+
3945
ClassLoader parentClassLoader = getClass().getClassLoader();
40-
CustomClassLoader classLoader = new CustomClassLoader(parentClassLoader);
46+
ClassLoader classLoader = new CustomSmartClassLoader(parentClassLoader);
4147
Class<?> myClass = parentClassLoader.loadClass(MyConfig.class.getName());
42-
configurationClassEnhancer.enhance(myClass, parentClassLoader);
43-
Class<?> myReloadedClass = classLoader.loadClass(MyConfig.class.getName());
44-
Class<?> enhancedReloadedClass = configurationClassEnhancer.enhance(myReloadedClass, classLoader);
45-
assertThat(enhancedReloadedClass.getClassLoader()).isEqualTo(classLoader);
48+
Class<?> enhancedClass = configurationClassEnhancer.enhance(myClass, parentClassLoader);
49+
assertThat(myClass).isAssignableFrom(enhancedClass);
50+
51+
myClass = classLoader.loadClass(MyConfig.class.getName());
52+
enhancedClass = configurationClassEnhancer.enhance(myClass, classLoader);
53+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
54+
assertThat(myClass).isAssignableFrom(enhancedClass);
55+
}
56+
57+
@Test
58+
void withPublicClass() {
59+
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
60+
61+
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
62+
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
63+
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
64+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
65+
66+
classLoader = new OverridingClassLoader(getClass().getClassLoader());
67+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
68+
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
69+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
70+
71+
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
72+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
73+
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
74+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
75+
76+
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
77+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithPublicClass.class, classLoader);
78+
assertThat(MyConfigWithPublicClass.class).isAssignableFrom(enhancedClass);
79+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
80+
}
81+
82+
@Test
83+
void withNonPublicClass() {
84+
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
85+
86+
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
87+
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
88+
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
89+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
90+
91+
classLoader = new OverridingClassLoader(getClass().getClassLoader());
92+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
93+
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
94+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
95+
96+
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
97+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
98+
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
99+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
100+
101+
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
102+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicClass.class, classLoader);
103+
assertThat(MyConfigWithNonPublicClass.class).isAssignableFrom(enhancedClass);
104+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
105+
}
106+
107+
@Test
108+
void withNonPublicMethod() {
109+
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
110+
111+
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
112+
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
113+
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
114+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader);
115+
116+
classLoader = new OverridingClassLoader(getClass().getClassLoader());
117+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
118+
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
119+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
120+
121+
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
122+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
123+
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
124+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
125+
126+
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
127+
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicMethod.class, classLoader);
128+
assertThat(MyConfigWithNonPublicMethod.class).isAssignableFrom(enhancedClass);
129+
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
46130
}
47131

48132

49133
@Configuration
50134
static class MyConfig {
51135

136+
@Bean
137+
String myBean() {
138+
return "bean";
139+
}
140+
}
141+
142+
143+
@Configuration
144+
public static class MyConfigWithPublicClass {
145+
52146
@Bean
53147
public String myBean() {
54148
return "bean";
55149
}
56150
}
57151

58152

59-
static class CustomClassLoader extends SecureClassLoader implements SmartClassLoader {
153+
@Configuration
154+
static class MyConfigWithNonPublicClass {
60155

61-
CustomClassLoader(ClassLoader parent) {
156+
@Bean
157+
public String myBean() {
158+
return "bean";
159+
}
160+
}
161+
162+
163+
@Configuration
164+
public static class MyConfigWithNonPublicMethod {
165+
166+
@Bean
167+
String myBean() {
168+
return "bean";
169+
}
170+
}
171+
172+
173+
static class CustomSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
174+
175+
CustomSmartClassLoader(ClassLoader parent) {
62176
super(parent);
63177
}
64178

@@ -82,6 +196,29 @@ protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundE
82196
public boolean isClassReloadable(Class<?> clazz) {
83197
return clazz.getName().contains("MyConfig");
84198
}
199+
200+
@Override
201+
public ClassLoader getOriginalClassLoader() {
202+
return getParent();
203+
}
204+
205+
@Override
206+
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
207+
return defineClass(name, b, 0, b.length, protectionDomain);
208+
}
209+
}
210+
211+
212+
static class BasicSmartClassLoader extends SecureClassLoader implements SmartClassLoader {
213+
214+
BasicSmartClassLoader(ClassLoader parent) {
215+
super(parent);
216+
}
217+
218+
@Override
219+
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
220+
return defineClass(name, b, 0, b.length, protectionDomain);
221+
}
85222
}
86223

87224
}

0 commit comments

Comments
 (0)