Browse Source

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 <yongjunh@apache.org>
pull/35344/head
yongjunhong 4 months ago
parent
commit
44c08b7918
No known key found for this signature in database
GPG Key ID: 2CB7F4B427C9E5B8
  1. 26
      spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
  2. 43
      spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

26
spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java

@ -79,6 +79,7 @@ import org.springframework.util.ObjectUtils; @@ -79,6 +79,7 @@ import org.springframework.util.ObjectUtils;
* @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 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -233,9 +234,11 @@ class CglibAopProxy implements AopProxy, Serializable {
}
}
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 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -243,6 +246,23 @@ class CglibAopProxy implements AopProxy, Serializable {
}
}
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();

43
spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

@ -50,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException @@ -50,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* @author Rob Harrop
* @author Ramnivas Laddad
* @author Chris Beams
* @author Yongjun Hong
*/
class CglibProxyTests extends AbstractAopProxyTests {
@ -429,6 +430,48 @@ class CglibProxyTests extends AbstractAopProxyTests { @@ -429,6 +430,48 @@ class CglibProxyTests extends AbstractAopProxyTests {
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 {

Loading…
Cancel
Save