From f9be717602b954609bcba0a36495208d48d52a65 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 23 Oct 2023 17:32:25 +0200 Subject: [PATCH] Avoid getObjectType exception for uninitialized ProxyFactoryBean Closes gh-31473 --- .../aop/framework/ProxyFactoryBean.java | 22 +++++++-- .../aop/framework/ProxyFactoryBeanTests.java | 46 +++++++++++-------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java index f963721c58..6556e530df 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java @@ -265,18 +265,32 @@ public class ProxyFactoryBean extends ProxyCreatorSupport * Return the type of the proxy. Will check the singleton instance if * already created, else fall back to the proxy interface (in case of just * a single one), the target bean type, or the TargetSource's target class. - * @see org.springframework.aop.TargetSource#getTargetClass + * @see org.springframework.aop.framework.AopProxy#getProxyClass */ @Override + @Nullable public Class getObjectType() { synchronized (this) { if (this.singletonInstance != null) { return this.singletonInstance.getClass(); } } - // This might be incomplete since it potentially misses introduced interfaces - // from Advisors that will be lazily retrieved via setInterceptorNames. - return createAopProxy().getProxyClass(this.proxyClassLoader); + try { + // This might be incomplete since it potentially misses introduced interfaces + // from Advisors that will be lazily retrieved via setInterceptorNames. + return createAopProxy().getProxyClass(this.proxyClassLoader); + } + catch (AopConfigException ex) { + if (getTargetClass() == null) { + if (logger.isDebugEnabled()) { + logger.debug("Failed to determine early proxy class: " + ex.getMessage()); + } + return null; + } + else { + throw ex; + } + } } @Override diff --git a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java index 9d1c9b8987..bc051b7fbc 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java @@ -139,22 +139,24 @@ public class ProxyFactoryBeanTests { private void testDoubleTargetSourceIsRejected(String name) { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(DBL_TARGETSOURCE_CONTEXT, CLASS)); + assertThatExceptionOfType(BeanCreationException.class).as("Should not allow TargetSource to be specified in interceptorNames as well as targetSource property") - .isThrownBy(() -> bf.getBean(name)) - .havingCause() - .isInstanceOf(AopConfigException.class) - .withMessageContaining("TargetSource"); + .isThrownBy(() -> bf.getBean(name)) + .havingCause() + .isInstanceOf(AopConfigException.class) + .withMessageContaining("TargetSource"); } @Test public void testTargetSourceNotAtEndOfInterceptorNamesIsRejected() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(NOTLAST_TARGETSOURCE_CONTEXT, CLASS)); + assertThatExceptionOfType(BeanCreationException.class).as("TargetSource or non-advised object must be last in interceptorNames") - .isThrownBy(() -> bf.getBean("targetSourceNotLast")) - .havingCause() - .isInstanceOf(AopConfigException.class) - .withMessageContaining("interceptorNames"); + .isThrownBy(() -> bf.getBean("targetSourceNotLast")) + .havingCause() + .isInstanceOf(AopConfigException.class) + .withMessageContaining("interceptorNames"); } @Test @@ -171,7 +173,7 @@ public class ProxyFactoryBeanTests { assertThat(cba.getCalls()).isEqualTo(1); ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&directTarget"); - assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue(); + assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class); } @Test @@ -181,7 +183,7 @@ public class ProxyFactoryBeanTests { ITestBean tb = (ITestBean) bf.getBean("viaTargetSource"); assertThat(tb.getName()).isEqualTo("Adam"); ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&viaTargetSource"); - assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue(); + assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class); } @Test @@ -190,11 +192,15 @@ public class ProxyFactoryBeanTests { new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS)); ITestBean tb = (ITestBean) bf.getBean("noTarget"); - assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> - tb.getName()) - .withMessage("getName"); + assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(tb::getName).withMessage("getName"); FactoryBean pfb = (ProxyFactoryBean) bf.getBean("&noTarget"); - assertThat(ITestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue(); + assertThat(pfb.getObjectType()).isAssignableTo(ITestBean.class); + } + + @Test + public void testGetObjectTypeOnUninitializedFactoryBean() { + ProxyFactoryBean pfb = new ProxyFactoryBean(); + assertThat(pfb.getObjectType()).isNull(); } /** @@ -227,12 +233,12 @@ public class ProxyFactoryBeanTests { @Test public void testPrototypeInstancesAreNotEqual() { - assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue(); + assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class); ITestBean test2 = (ITestBean) factory.getBean("prototype"); ITestBean test2_1 = (ITestBean) factory.getBean("prototype"); assertThat(test2).as("Prototype instances !=").isNotSameAs(test2_1); assertThat(test2).as("Prototype instances equal").isEqualTo(test2_1); - assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue(); + assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class); } /** @@ -291,13 +297,13 @@ public class ProxyFactoryBeanTests { @Test public void testCanGetFactoryReferenceAndManipulate() { ProxyFactoryBean config = (ProxyFactoryBean) factory.getBean("&test1"); - assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue(); - assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue(); + assertThat(config.getObjectType()).isAssignableTo(ITestBean.class); + assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class); // Trigger lazy initialization. config.getObject(); assertThat(config.getAdvisors().length).as("Have one advisors").isEqualTo(1); - assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue(); - assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue(); + assertThat(config.getObjectType()).isAssignableTo(ITestBean.class); + assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class); ITestBean tb = (ITestBean) factory.getBean("test1"); // no exception