Repository navigation
ConfigurationClassEnhancer should explicitly set custom ClassLoader on CGLIB Enhancer (aligned with CglibAopProxy) #34274
Copy link
Copy link
Closed
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchesAn issue that has been backported to maintenance branchesstatus: feedback-providedFeedback has been providedFeedback has been providedtype: bugA general bugA general bug
Milestone
Description
Activity
- addedstatus: waiting-for-triageAn issue we've not yet triaged or decided onAn issue we've not yet triaged or decided on
on Jan 16, 2025 @AlexanderShchelkunov Thanks for the report. Please move all that text into a sample we can actually run ourselves. You can attach a zip to this issue or push the code to a GitHub repository.
- addedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continue
on Jan 20, 2025 AlexanderShchelkunov commented
on Jan 20, 2025 AuthorMore actions@AlexanderShchelkunov Thanks for the report. Please move all that text into a sample we can actually run ourselves. You can attach a zip to this issue or push the code to a GitHub repository.
Hi @snicoll,
Please see the attached file.- addedstatus: feedback-providedFeedback has been providedFeedback has been providedand removedstatus: waiting-for-feedbackWe need additional information before we can continueWe need additional information before we can continue
on Jan 20, 2025 - changed the title
[-]java.lang.ClassCastException: class XXX$$SpringCGLIB$$0 cannot be cast to class org.springframework.cglib.proxy.Factory on app startup with custom classloader[/-][+]`ClassCastException`: `XXX$$SpringCGLIB$$0` cannot be cast to `org.springframework.cglib.proxy.Factory` with custom `ClassLoader`[/+]on Jan 21, 2025 - addedin: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)
on Feb 3, 2025 - addedtype: bugA general bugA general bugand removedstatus: waiting-for-triageAn issue we've not yet triaged or decided onAn issue we've not yet triaged or decided on
on Feb 3, 2025 - changed the title
[-]`ClassCastException`: `XXX$$SpringCGLIB$$0` cannot be cast to `org.springframework.cglib.proxy.Factory` with custom `ClassLoader`[/-][+]`ConfigurationClassEnhancer` should explicitly set custom `ClassLoader` on CGLIB `Enhancer` (aligned with `CglibAopProxy`)[/+]on Feb 3, 2025 - addedstatus: backportedAn issue that has been backported to maintenance branchesAn issue that has been backported to maintenance branchesand removed
on Feb 3, 2025 - added a commit that references this issue
on Feb 3, 2025 - added a commit that references this issue
on Feb 3, 2025 Reopening due to a side effect with package-visible superclasses not being accessible.
- added a commit that references this issue
on Feb 11, 2025 - added a commit that references this issue
on Feb 11, 2025
Metadata
Metadata
Assignees
Labels
in: coreIssues in core modules (aop, beans, core, context, expression)Issues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchesAn issue that has been backported to maintenance branchesstatus: feedback-providedFeedback has been providedFeedback has been providedtype: bugA general bugA general bug
demo.zip
Description
Hello!
I have a Spring Boot application, that supports 3rd party plugins. Because of that I need to use a custom class loader. But if there is a
@Configurationthat has@Lazyannotation (when Spring has to create a proxy), the app will not start. See the possible way to fix at the bottom.Environment
Spring Boot: 3.3.3
Spring Framework: 6.1.12
Java: JDK 17
Steps to Reproduce
Start the app
Error Message
Minimal sample application
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.3.3' id 'io.spring.dependency-management' version '1.1.7' } group = 'com.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework:spring-core' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { useJUnitPlatform() }DemoApplication.java
SomeConfig.java
SomeService.java
Possible way to fix
This happens because Enhancer, that creates Configuration does not respect provided custom class loader.
In
ConfigurationClassEnhancerin method newEnhancer it does not call enhancer.setClassLoader(classLoader)In the same time CglibAopProxy respects provided custom class loader, it sets classloader in buildProxy method
There is also cache in AbstractClassGenerator, where key is ClassLoader:
private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<>();When Spring loads configuration class the first time, it does not use custom classloader and key is the default classloader (method create in AbstractClassGenerator).
When Spring creates cglib proxy for the configuration class, it calles
createagain, but this time custom class loader set to classLoader field, so the key in the CACHE is different. And this causes the issue.This probably could be fixed if enhanchers for the same class will always use the same class loader.
For example you can set classloader in ConfigurationClassEnhancer -> newEnhancer method. I tried to do it in debug and it seems to work.