From 44c08b7918fbe18819d5e4b65d2445db0244d1b4 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Tue, 19 Aug 2025 10:40:55 +0900 Subject: [PATCH] Fall back to JDK proxy if CGLIB proxy creation fails Prior to this change, if the default CGLIB proxy creation failed for a non-subclassable target class (e.g., due to a private constructor), the ApplicationContext would fail to start. Signed-off-by: yongjunhong --- .../aop/framework/CglibAopProxy.java | 26 +++++++++-- .../aop/framework/CglibProxyTests.java | 43 +++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java index 8ed496c3dade..baef4b61d156 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java @@ -79,6 +79,7 @@ * @author Chris Beams * @author Dave Syer * @author Sebastien Deleuze + * @author Yongjun Hong * @see org.springframework.cglib.proxy.Enhancer * @see AdvisedSupport#setProxyTargetClass * @see DefaultAopProxyFactory @@ -233,9 +234,11 @@ private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) } } catch (CodeGenerationException | IllegalArgumentException ex) { - throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + - ": Common causes of this problem include using a final class or a non-visible class", - ex); + if (logger.isWarnEnabled()) { + logger.warn("CGLIB subclass failed: " + ex.getMessage() + + " - falling back to JDK dynamic proxy for " + this.advised.getTargetSource(), ex); + } + return handleCglibFailure(classLoader, ex); } catch (Throwable ex) { // TargetSource.getTarget() failed @@ -243,6 +246,23 @@ private Object buildProxy(@Nullable ClassLoader classLoader, boolean classOnly) } } + private Object handleCglibFailure(@Nullable ClassLoader classLoader, Exception originalException) { + Class[] interfaces = this.advised.getProxiedInterfaces(); + + if (interfaces.length == 0) { + throw new AopConfigException("Could not generate CGLIB subclass of " + + this.advised.getTargetClass() + " and JDK proxy fallback not possible", originalException); + } + try { + JdkDynamicAopProxy jdkProxy = new JdkDynamicAopProxy(this.advised); + return jdkProxy.getProxy(classLoader); + } + catch (Throwable ex) { + throw new AopConfigException("Could not generate CGLIB subclass of " + + this.advised.getTargetClass() + " and JDK proxy fallback failed for interfaces " + ex); + } + } + protected Class createProxyClass(Enhancer enhancer) { enhancer.setInterceptDuringConstruction(false); return enhancer.createClass(); diff --git a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java index 626c324079ff..69670078a953 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java @@ -50,6 +50,7 @@ * @author Rob Harrop * @author Ramnivas Laddad * @author Chris Beams + * @author Yongjun Hong */ class CglibProxyTests extends AbstractAopProxyTests { @@ -429,6 +430,48 @@ void varargsWithEnumArray() { assertThat(proxy.doWithVarargs(MyEnum.A, MyOtherEnum.C)).isTrue(); } + @Test + void testProxyCreationAndPrivateAccess() { + ProxyFactory proxyFactory = new ProxyFactory(new TestServiceImpl()); + proxyFactory.setProxyTargetClass(true); + + TestService proxy = (TestService) proxyFactory.getProxy(); + + String result = proxy.publicMethod(); + assertThat(result).isEqualTo("Private Access Success"); + } + + @Test + void testProxyCreationWithoutInterfaceShouldThrowException() { + ProxyFactory proxyFactory = new ProxyFactory(new TestServiceImplWithOutInterface()); + proxyFactory.setProxyTargetClass(true); + + assertThatExceptionOfType(AopConfigException.class) + .isThrownBy(proxyFactory::getProxy) + .withMessageContaining("Could not generate CGLIB subclass of"); + } + + interface TestService { + String publicMethod(); + } + + static class TestServiceImpl implements TestService { + private TestServiceImpl() { } + + @Override + public String publicMethod() { + return "Private Access Success"; + } + } + + static class TestServiceImplWithOutInterface { + + private TestServiceImplWithOutInterface() { } + + public String publicMethod() { + return "Private Access Success"; + } + } public static class MyBean {