From 1ba2dfbdbf2f089e0d2956ecaf16be5371ce2c16 Mon Sep 17 00:00:00 2001 From: malik ait Date: Thu, 23 Apr 2026 19:12:05 +0100 Subject: [PATCH 1/2] Update ObjenesisCglibAopProxy.java Signed-off-by: malik ait --- .../aop/framework/ObjenesisCglibAopProxy.java | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java index c516dac616ef..fa2973d4a7f9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ObjenesisCglibAopProxy.java @@ -21,6 +21,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cglib.core.SpringNamingPolicy; import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.Factory; @@ -67,13 +68,11 @@ protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callb proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache()); } catch (Throwable ex) { - logger.debug("Unable to instantiate proxy using Objenesis, " + - "falling back to regular proxy construction", ex); + logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular construction", ex); } } if (proxyInstance == null) { - // Regular instantiation via default constructor... try { Constructor ctor = (this.constructorArgs != null ? proxyClass.getDeclaredConstructor(this.constructorArgTypes) : @@ -83,12 +82,73 @@ protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callb ctor.newInstance(this.constructorArgs) : ctor.newInstance()); } catch (Throwable ex) { - throw new AopConfigException("Unable to instantiate proxy using Objenesis, " + - "and regular proxy instantiation via default constructor fails as well", ex); + throw new AopConfigException("Unable to instantiate proxy", ex); } } + /* + * Workaround for issue #30985. + * + * In native image mode, SpringNamingPolicy can generate colliding class names + * (all ending with $$SpringCGLIB$$0) when multiple incompatible CGLIB proxies + * are needed for the same bean (e.g. @Lazy constructor proxy + AOP class proxy). + * + * This leads to a ClassCastException when setCallbacks() is called because + * the pre-generated proxy class does not match the required callback layout. + * + * This fallback tries the next numbered proxy class ($$SpringCGLIB$$1, + * $$SpringCGLIB$$2, ...) from the classpath until a compatible one is found. + * + * Note: This is a targeted runtime workaround because changing SpringNamingPolicy + * would be a breaking change for many existing components. + */ + // === Safe fallback only for SpringNamingPolicy === + if (enhancer.getNamingPolicy() == SpringNamingPolicy.INSTANCE) { + try { + ((Factory) proxyInstance).setCallbacks(callbacks); + return proxyInstance; + } + catch (ClassCastException ex) { + // Name collision detected ? try next numbered proxy ($$1, $$2, ...) + String className = proxyClass.getName(); + int lastDoubleDollar = className.lastIndexOf("$$"); + + if (lastDoubleDollar == -1) { + throw ex; // not a Spring CGLIB proxy ? rethrow + } + + String base = className.substring(0, lastDoubleDollar + 2); + String numberStr = className.substring(lastDoubleDollar + 2); + int counter = Integer.parseInt(numberStr); + + // Safety limit to prevent infinite loop + for (int i = 0; i < 10; i++) { // max 10 attempts + counter++; + String nextName = base + counter; + + try { + Class nextProxyClass = Class.forName(nextName); + proxyInstance = (this.constructorArgs != null ? + nextProxyClass.getDeclaredConstructor(this.constructorArgTypes) + .newInstance(this.constructorArgs) : + nextProxyClass.getDeclaredConstructor().newInstance()); + + ((Factory) proxyInstance).setCallbacks(callbacks); + logger.debug("Used fallback proxy class: {}"); + return proxyInstance; + } + catch (Exception ignored) { + // Class does not exist or cannot be instantiated ? try next number + } + } + + // If we reach here, no suitable proxy was found + throw new AopConfigException("Could not find a compatible CGLIB proxy for " + className, ex); + } + } + else { + ((Factory) proxyInstance).setCallbacks(callbacks); + } - ((Factory) proxyInstance).setCallbacks(callbacks); return proxyInstance; } From 3617684bda95e53e78da12de42fafa3ef3f8f4e8 Mon Sep 17 00:00:00 2001 From: malik ait Date: Thu, 23 Apr 2026 19:17:29 +0100 Subject: [PATCH 2/2] Update InstanceSupplierCodeGenerator.java Signed-off-by: malik ait --- .../beans/factory/aot/InstanceSupplierCodeGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java index 1c295379f20f..a4c067b7ca44 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java @@ -439,8 +439,8 @@ public void registerRuntimeHints(RuntimeHints runtimeHints, Executable executabl private void registerProxyIfNecessary(RuntimeHints runtimeHints, DependencyDescriptor dependencyDescriptor) { Class proxyType = this.candidateResolver.getLazyResolutionProxyClass(dependencyDescriptor, null); - if (proxyType != null && Proxy.isProxyClass(proxyType)) { - runtimeHints.proxies().registerJdkProxy(proxyType.getInterfaces()); + if (proxyType != null ) { + ClassHintUtils.registerProxyIfNecessary(proxyType, runtimeHints); } } }