From 3bd96f72a69e872085eb664ca392f98aa8a9a6db Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 7 Jul 2025 14:26:19 +0200 Subject: [PATCH] Leniently tolerate null bean as aspect instance Closes gh-35074 --- .../aop/aspectj/AbstractAspectJAdvice.java | 19 ++++++++++++------ .../BeanFactoryAspectInstanceFactory.java | 8 +++++++- .../AspectJAutoProxyCreatorTests.java | 20 +++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java index 4594fb7e028..15af8421702 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java @@ -618,28 +618,35 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence * @return the invocation result * @throws Throwable in case of invocation failure */ - protected Object invokeAdviceMethod( - @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex) - throws Throwable { + protected @Nullable Object invokeAdviceMethod(@Nullable JoinPointMatch jpMatch, + @Nullable Object returnValue, @Nullable Throwable ex) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); } // As above, but in this case we are given the join point. - protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, + protected @Nullable Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable t) throws Throwable { return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); } - protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { + protected @Nullable Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { Object[] actualArgs = args; if (this.aspectJAdviceMethod.getParameterCount() == 0) { actualArgs = null; } + Object aspectInstance = this.aspectInstanceFactory.getAspectInstance(); + if (aspectInstance.equals(null)) { + // Possibly a NullBean -> simply proceed if necessary. + if (getJoinPoint() instanceof ProceedingJoinPoint pjp) { + return pjp.proceed(); + } + return null; + } try { ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); - return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); + return this.aspectJAdviceMethod.invoke(aspectInstance, actualArgs); } catch (IllegalArgumentException ex) { throw new AopInvocationException("Mismatch on arguments to advice method [" + diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java index 23b4cf88f68..7ce1f8ad394 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java @@ -19,6 +19,7 @@ package org.springframework.aop.aspectj.annotation; import java.io.Serializable; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; @@ -130,7 +131,12 @@ public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInst Class type = this.beanFactory.getType(this.name); if (type != null) { if (Ordered.class.isAssignableFrom(type) && this.beanFactory.isSingleton(this.name)) { - return ((Ordered) this.beanFactory.getBean(this.name)).getOrder(); + try { + return this.beanFactory.getBean(this.name, Ordered.class).getOrder(); + } + catch (BeanNotOfRequiredTypeException ex) { + // Not actually implementing Ordered -> possibly a NullBean. + } } return OrderUtils.getOrder(type, Ordered.LOWEST_PRECEDENCE); } diff --git a/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests.java b/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests.java index 17713812583..179b5d862f3 100644 --- a/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests.java +++ b/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AspectJAutoProxyCreatorTests.java @@ -364,6 +364,16 @@ class AspectJAutoProxyCreatorTests { } } + @Test + void nullAdviceIsSkipped() { + try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ProxyWithNullAdviceConfig.class)) { + @SuppressWarnings("unchecked") + Supplier supplier = context.getBean(Supplier.class); + assertThat(AopUtils.isAopProxy(supplier)).as("AOP proxy").isTrue(); + assertThat(supplier.get()).isEqualTo("lambda"); + } + } + private ClassPathXmlApplicationContext newContext(String fileSuffix) { return new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-" + fileSuffix, getClass()); } @@ -627,6 +637,16 @@ class ProxyTargetClassFalseConfig extends AbstractProxyTargetClassConfig { class ProxyTargetClassTrueConfig extends AbstractProxyTargetClassConfig { } +@Configuration(proxyBeanMethods = false) +@EnableAspectJAutoProxy(proxyTargetClass = true) +class ProxyWithNullAdviceConfig extends AbstractProxyTargetClassConfig { + + @Override + SupplierAdvice supplierAdvice() { + return null; + } +} + @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) class PerTargetProxyTargetClassTrueConfig {