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 26020a327a29..e43f9d14af26 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 @@ -272,14 +272,18 @@ public void setArgumentNamesFromStringArray(@Nullable String... argumentNames) { } if (this.aspectJAdviceMethod.getParameterCount() == this.argumentNames.length + 1) { // May need to add implicit join point arg name... - Class firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0]; - if (firstArgType == JoinPoint.class || - firstArgType == ProceedingJoinPoint.class || - firstArgType == JoinPoint.StaticPart.class) { - @Nullable String[] oldNames = this.argumentNames; - this.argumentNames = new String[oldNames.length + 1]; - this.argumentNames[0] = "THIS_JOIN_POINT"; - System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length); + for (int i = 0; i < this.aspectJAdviceMethod.getParameterCount(); i++) { + Class argType = this.aspectJAdviceMethod.getParameterTypes()[i]; + if (argType == JoinPoint.class || + argType == ProceedingJoinPoint.class || + argType == JoinPoint.StaticPart.class) { + @Nullable String[] oldNames = this.argumentNames; + this.argumentNames = new String[oldNames.length + 1]; + System.arraycopy(oldNames, 0, this.argumentNames, 0, i); + this.argumentNames[i] = "THIS_JOIN_POINT"; + System.arraycopy(oldNames, i, this.argumentNames, i + 1, oldNames.length - i); + break; + } } } } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AbstractAspectJAdviceTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AbstractAspectJAdviceTests.java new file mode 100644 index 000000000000..0cba61aacd6c --- /dev/null +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AbstractAspectJAdviceTests.java @@ -0,0 +1,132 @@ +/* + * Copyright 2002-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.aop.aspectj; + +import java.io.Serial; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link AbstractAspectJAdvice}. + * + * @author Joshua Chen + */ +class AbstractAspectJAdviceTests { + + @Test + void setArgumentNamesFromStringArray_withJoinPointAsFirstParameter() { + AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsFirstParameter"); + assertArgumentNamesFromStringArray(advice); + } + + @Test + void setArgumentNamesFromStringArray_withJoinPointAsLastParameter() { + AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsLastParameter"); + assertThat(getArgumentNames(advice)[0]).isEqualTo("arg1"); + assertThat(getArgumentNames(advice)[1]).isEqualTo("arg2"); + assertThat(getArgumentNames(advice)[2]).isEqualTo("THIS_JOIN_POINT"); + } + + @Test + void setArgumentNamesFromStringArray_withJoinPointAsMiddleParameter() { + AbstractAspectJAdvice advice = getAspectJAdvice("methodWithJoinPointAsMiddleParameter"); + assertThat(getArgumentNames(advice)[0]).isEqualTo("arg1"); + assertThat(getArgumentNames(advice)[1]).isEqualTo("THIS_JOIN_POINT"); + assertThat(getArgumentNames(advice)[2]).isEqualTo("arg2"); + } + + @Test + void setArgumentNamesFromStringArray_withProceedingJoinPoint() { + AbstractAspectJAdvice advice = getAspectJAdvice("methodWithProceedingJoinPoint"); + assertArgumentNamesFromStringArray(advice); + } + + @Test + void setArgumentNamesFromStringArray_withStaticPart() { + AbstractAspectJAdvice advice = getAspectJAdvice("methodWithStaticPart"); + assertArgumentNamesFromStringArray(advice); + } + + private void assertArgumentNamesFromStringArray(AbstractAspectJAdvice advice) { + assertThat(getArgumentNames(advice)[0]).isEqualTo("THIS_JOIN_POINT"); + assertThat(getArgumentNames(advice)[1]).isEqualTo("arg1"); + assertThat(getArgumentNames(advice)[2]).isEqualTo("arg2"); + } + + private @NotNull AbstractAspectJAdvice getAspectJAdvice(final String methodName) { + AbstractAspectJAdvice advice = new TestAspectJAdvice(getMethod(methodName), mock(AspectJExpressionPointcut.class), mock(AspectInstanceFactory.class)); + advice.setArgumentNamesFromStringArray("arg1", "arg2"); + return advice; + } + + private Method getMethod(final String name) { + return Arrays.stream(this.getClass().getDeclaredMethods()).filter(m -> m.getName().equals(name)).findFirst().orElseThrow(); + } + + private String[] getArgumentNames(final AbstractAspectJAdvice advice) { + try { + Field field = AbstractAspectJAdvice.class.getDeclaredField("argumentNames"); + field.setAccessible(true); + return (String[]) field.get(advice); + } + catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static class TestAspectJAdvice extends AbstractAspectJAdvice { + @Serial private static final long serialVersionUID = 1L; + + public TestAspectJAdvice(Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) { + super(aspectJAdviceMethod, pointcut, aspectInstanceFactory); + } + + @Override + public boolean isBeforeAdvice() { + return false; + } + + @Override + public boolean isAfterAdvice() { + return false; + } + } + + void methodWithJoinPointAsFirstParameter(JoinPoint joinPoint, String arg1, String arg2) { + } + + void methodWithJoinPointAsLastParameter(String arg1, String arg2, JoinPoint joinPoint) { + } + + void methodWithJoinPointAsMiddleParameter(String arg1, JoinPoint joinPoint, String arg2) { + } + + void methodWithProceedingJoinPoint(ProceedingJoinPoint joinPoint, String arg1, String arg2) { + } + + void methodWithStaticPart(JoinPoint.StaticPart staticPart, String arg1, String arg2) { + } +}