Skip to content

Commit e73d68b

Browse files
committed
Select most specific advice method in case of override
Closes gh-32865 (cherry picked from commit ea596aa)
1 parent 0ca47e5 commit e73d68b

File tree

2 files changed

+31
-26
lines changed

2 files changed

+31
-26
lines changed

spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,6 +50,7 @@
5050
import org.springframework.core.convert.converter.Converter;
5151
import org.springframework.core.convert.converter.ConvertingComparator;
5252
import org.springframework.lang.Nullable;
53+
import org.springframework.util.ClassUtils;
5354
import org.springframework.util.ReflectionUtils;
5455
import org.springframework.util.ReflectionUtils.MethodFilter;
5556
import org.springframework.util.StringUtils;
@@ -133,17 +134,19 @@ public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan
133134

134135
List<Advisor> advisors = new ArrayList<>();
135136
for (Method method : getAdvisorMethods(aspectClass)) {
136-
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
137-
// to getAdvisor(...) to represent the "current position" in the declared methods list.
138-
// However, since Java 7 the "current position" is not valid since the JDK no longer
139-
// returns declared methods in the order in which they are declared in the source code.
140-
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
141-
// discovered via reflection in order to support reliable advice ordering across JVM launches.
142-
// Specifically, a value of 0 aligns with the default value used in
143-
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
144-
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
145-
if (advisor != null) {
146-
advisors.add(advisor);
137+
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
138+
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
139+
// to getAdvisor(...) to represent the "current position" in the declared methods list.
140+
// However, since Java 7 the "current position" is not valid since the JDK no longer
141+
// returns declared methods in the order in which they are declared in the source code.
142+
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
143+
// discovered via reflection in order to support reliable advice ordering across JVM launches.
144+
// Specifically, a value of 0 aligns with the default value used in
145+
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
146+
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
147+
if (advisor != null) {
148+
advisors.add(advisor);
149+
}
147150
}
148151
}
149152

spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java

+16-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@
3838
import org.aspectj.lang.annotation.DeclarePrecedence;
3939
import org.aspectj.lang.annotation.Pointcut;
4040
import org.aspectj.lang.reflect.MethodSignature;
41-
import org.junit.jupiter.api.Disabled;
4241
import org.junit.jupiter.api.Test;
4342

4443
import org.springframework.aop.Advisor;
@@ -84,15 +83,15 @@ abstract class AbstractAspectJAdvisorFactoryTests {
8483
@Test
8584
void rejectsPerCflowAspect() {
8685
assertThatExceptionOfType(AopConfigException.class)
87-
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
88-
.withMessageContaining("PERCFLOW");
86+
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
87+
.withMessageContaining("PERCFLOW");
8988
}
9089

9190
@Test
9291
void rejectsPerCflowBelowAspect() {
9392
assertThatExceptionOfType(AopConfigException.class)
94-
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
95-
.withMessageContaining("PERCFLOWBELOW");
93+
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
94+
.withMessageContaining("PERCFLOWBELOW");
9695
}
9796

9897
@Test
@@ -363,7 +362,7 @@ void introductionOnTargetImplementingInterface() {
363362
assertThat(lockable.locked()).as("Already locked").isTrue();
364363
lockable.lock();
365364
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
366-
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> lockable.unlock());
365+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
367366
}
368367

369368
@Test
@@ -389,9 +388,7 @@ void introductionBasedOnAnnotationMatch() { // gh-9980
389388
assertThat(lockable.locked()).isTrue();
390389
}
391390

392-
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
393391
@Test
394-
@Disabled
395392
void introductionWithArgumentBinding() {
396393
TestBean target = new TestBean();
397394

@@ -648,7 +645,7 @@ void getAge() {
648645
static class NamedPointcutAspectWithFQN {
649646

650647
@SuppressWarnings("unused")
651-
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
648+
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
652649

653650
@Around("org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactoryTests.CommonPointcuts.getAge()()")
654651
int changeReturnValue(ProceedingJoinPoint pjp) {
@@ -765,16 +762,22 @@ Object echo(Object obj) throws Exception {
765762

766763

767764
@Aspect
768-
class DoublingAspect {
765+
static class DoublingAspect {
769766

770767
@Around("execution(* getAge())")
771768
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
772769
return ((int) pjp.proceed()) * 2;
773770
}
774771
}
775772

773+
776774
@Aspect
777-
class IncrementingAspect extends DoublingAspect {
775+
static class IncrementingAspect extends DoublingAspect {
776+
777+
@Override
778+
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
779+
return ((int) pjp.proceed()) * 2;
780+
}
778781

779782
@Around("execution(* getAge())")
780783
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
@@ -783,7 +786,6 @@ public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
783786
}
784787

785788

786-
787789
@Aspect
788790
private static class InvocationTrackingAspect {
789791

@@ -1083,7 +1085,7 @@ class PerThisAspect {
10831085

10841086
// Just to check that this doesn't cause problems with introduction processing
10851087
@SuppressWarnings("unused")
1086-
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
1088+
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
10871089

10881090
@Around("execution(int *.getAge())")
10891091
int returnCountAsAge() {

0 commit comments

Comments
 (0)