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-aop/src/main/java/org/springframework/aop/support/ClassFilters.java b/spring-aop/src/main/java/org/springframework/aop/support/ClassFilters.java index 5b7f3b055d..9436444777 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/ClassFilters.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/ClassFilters.java @@ -136,7 +136,6 @@ public abstract class ClassFilters { public String toString() { return getClass().getName() + ": " + Arrays.toString(this.filters); } - } @@ -177,7 +176,6 @@ public abstract class ClassFilters { public String toString() { return getClass().getName() + ": " + Arrays.toString(this.filters); } - } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java index 0da193a4ec..c519ffcb33 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java @@ -70,6 +70,7 @@ class BeanDefinitionPropertyValueCodeGenerator { BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods, @Nullable BiFunction customValueGenerator) { + this.generatedMethods = generatedMethods; this.delegates = new ArrayList<>(); if (customValueGenerator != null) { @@ -145,7 +146,6 @@ class BeanDefinitionPropertyValueCodeGenerator { @Nullable CodeBlock generateCode(Object value, ResolvableType type); - } @@ -165,7 +165,6 @@ class BeanDefinitionPropertyValueCodeGenerator { '\\', "\\\\" ); - @Override @Nullable public CodeBlock generateCode(Object value, ResolvableType type) { @@ -233,7 +232,6 @@ class BeanDefinitionPropertyValueCodeGenerator { } return null; } - } @@ -323,8 +321,8 @@ class BeanDefinitionPropertyValueCodeGenerator { this.emptyResult = emptyResult; } - @Override @SuppressWarnings("unchecked") + @Override @Nullable public CodeBlock generateCode(Object value, ResolvableType type) { if (this.collectionType.isInstance(value)) { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java index aa0a5ba5e9..32d0cb9451 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java @@ -445,7 +445,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean * @see #getResource * @see Lazy */ - protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) { + protected Object buildLazyResourceProxy(LookupElement element, @Nullable String requestingBeanName) { TargetSource ts = new TargetSource() { @Override public Class getTargetClass() { @@ -525,16 +525,16 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean String name = element.name; if (factory instanceof AutowireCapableBeanFactory autowireCapableBeanFactory) { - DependencyDescriptor descriptor = element.getDependencyDescriptor(); if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet<>(); - resource = autowireCapableBeanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null); + resource = autowireCapableBeanFactory.resolveDependency( + element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } else { - resource = autowireCapableBeanFactory.resolveBeanByName(name, descriptor); + resource = autowireCapableBeanFactory.resolveBeanByName(name, element.getDependencyDescriptor()); autowiredBeanNames = Collections.singleton(name); } } @@ -661,8 +661,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean } - - /** * Class representing injection information about an annotated field * or setter method, supporting the @Resource annotation. 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 diff --git a/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java index af0b72325f..daf59a936b 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessorTests.java @@ -543,6 +543,25 @@ public class CommonAnnotationBeanPostProcessorTests { assertThat(tb.getName()).isEqualTo("notLazyAnymore"); } + @Test + public void testLazyResolutionWithFallbackTypeMatch() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); + CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceCglibInjectionBean.class)); + bf.registerBeanDefinition("tb", new RootBeanDefinition(TestBean.class)); + + LazyResourceCglibInjectionBean bean = (LazyResourceCglibInjectionBean) bf.getBean("annotatedBean"); + assertThat(bf.containsSingleton("tb")).isFalse(); + bean.testBean.setName("notLazyAnymore"); + assertThat(bf.containsSingleton("tb")).isTrue(); + TestBean tb = (TestBean) bf.getBean("tb"); + assertThat(tb.getName()).isEqualTo("notLazyAnymore"); + } + public static class AnnotatedInitDestroyBean { diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 8f353e230b..2bfefa1c40 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -498,7 +498,6 @@ public class SpringFactoriesLoader { /** * Strategy for resolving constructor arguments based on their type. - * * @since 6.0 * @see ArgumentResolver#of(Class, Object) * @see ArgumentResolver#ofSupplied(Class, Supplier) @@ -595,13 +594,11 @@ public class SpringFactoriesLoader { */ static ArgumentResolver from(Function, Object> function) { return new ArgumentResolver() { - @SuppressWarnings("unchecked") @Override public T resolve(Class type) { return (T) function.apply(type); } - }; } } @@ -609,7 +606,6 @@ public class SpringFactoriesLoader { /** * Strategy for handling a failure that occurs when instantiating a factory. - * * @since 6.0 * @see FailureHandler#throwing() * @see FailureHandler#logging(Log) @@ -671,7 +667,7 @@ public class SpringFactoriesLoader { static FailureHandler handleMessage(BiConsumer, Throwable> messageHandler) { return (factoryType, factoryImplementationName, failure) -> { Supplier messageSupplier = () -> "Unable to instantiate factory class [%s] for factory type [%s]" - .formatted(factoryImplementationName, factoryType.getName()); + .formatted(factoryImplementationName, factoryType.getName()); messageHandler.accept(messageSupplier, failure); }; }