Skip to content

Commit eff9f5b

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

File tree

2 files changed

+63
-30
lines changed

2 files changed

+63
-30
lines changed

Diff for: 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-2020 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

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

+48-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 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.
@@ -37,7 +37,6 @@
3737
import org.aspectj.lang.annotation.DeclarePrecedence;
3838
import org.aspectj.lang.annotation.Pointcut;
3939
import org.aspectj.lang.reflect.MethodSignature;
40-
import org.junit.jupiter.api.Disabled;
4140
import org.junit.jupiter.api.Test;
4241
import test.aop.DefaultLockable;
4342
import test.aop.Lockable;
@@ -76,25 +75,24 @@ abstract class AbstractAspectJAdvisorFactoryTests {
7675

7776
/**
7877
* To be overridden by concrete test subclasses.
79-
* @return the fixture
8078
*/
8179
protected abstract AspectJAdvisorFactory getFixture();
8280

8381

8482
@Test
8583
void rejectsPerCflowAspect() {
86-
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
87-
getFixture().getAdvisors(
84+
assertThatExceptionOfType(AopConfigException.class)
85+
.isThrownBy(() -> getFixture().getAdvisors(
8886
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
89-
.withMessageContaining("PERCFLOW");
87+
.withMessageContaining("PERCFLOW");
9088
}
9189

9290
@Test
9391
void rejectsPerCflowBelowAspect() {
94-
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() ->
95-
getFixture().getAdvisors(
96-
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
97-
.withMessageContaining("PERCFLOWBELOW");
92+
assertThatExceptionOfType(AopConfigException.class)
93+
.isThrownBy(() -> getFixture().getAdvisors(
94+
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
95+
.withMessageContaining("PERCFLOWBELOW");
9896
}
9997

10098
@Test
@@ -385,8 +383,7 @@ void introductionOnTargetImplementingInterface() {
385383
assertThat(lockable.locked()).as("Already locked").isTrue();
386384
lockable.lock();
387385
assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
388-
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
389-
lockable.unlock());
386+
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
390387
}
391388

392389
@Test
@@ -413,9 +410,7 @@ void introductionBasedOnAnnotationMatch_SPR5307() {
413410
lockable.locked();
414411
}
415412

416-
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
417413
@Test
418-
@Disabled
419414
void introductionWithArgumentBinding() {
420415
TestBean target = new TestBean();
421416

@@ -523,6 +518,16 @@ void afterAdviceTypes() throws Exception {
523518
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
524519
}
525520

521+
@Test
522+
void parentAspect() {
523+
TestBean target = new TestBean("Jane", 42);
524+
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
525+
new IncrementingAspect(), "incrementingAspect");
526+
ITestBean proxy = (ITestBean) createProxy(target,
527+
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
528+
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
529+
}
530+
526531
@Test
527532
void failureWithoutExplicitDeclarePrecedence() {
528533
TestBean target = new TestBean();
@@ -647,7 +652,7 @@ public int getOrder() {
647652
static class NamedPointcutAspectWithFQN {
648653

649654
@SuppressWarnings("unused")
650-
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
655+
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
651656

652657
@Pointcut("execution(* getAge())")
653658
void getAge() {
@@ -767,6 +772,31 @@ Object echo(Object o) throws Exception {
767772
}
768773

769774

775+
@Aspect
776+
abstract static class DoublingAspect {
777+
778+
@Around("execution(* getAge())")
779+
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
780+
return ((int) pjp.proceed()) * 2;
781+
}
782+
}
783+
784+
785+
@Aspect
786+
static class IncrementingAspect extends DoublingAspect {
787+
788+
@Override
789+
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
790+
return ((int) pjp.proceed()) * 2;
791+
}
792+
793+
@Around("execution(* getAge())")
794+
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
795+
return ((int) pjp.proceed()) + 1;
796+
}
797+
}
798+
799+
770800
@Aspect
771801
private static class InvocationTrackingAspect {
772802

@@ -824,7 +854,7 @@ void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() {
824854

825855
@Around("getAge()")
826856
int preventExecution(ProceedingJoinPoint pjp) {
827-
return 666;
857+
return 42;
828858
}
829859
}
830860

@@ -844,7 +874,7 @@ void blowUpButDoesntMatterBecauseAroundAdviceWontLetThisBeInvoked() {
844874

845875
@Around("getAge()")
846876
int preventExecution(ProceedingJoinPoint pjp) {
847-
return 666;
877+
return 42;
848878
}
849879
}
850880

@@ -1066,7 +1096,7 @@ class PerThisAspect {
10661096

10671097
// Just to check that this doesn't cause problems with introduction processing
10681098
@SuppressWarnings("unused")
1069-
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
1099+
private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
10701100

10711101
@Around("execution(int *.getAge())")
10721102
int returnCountAsAge() {

0 commit comments

Comments
 (0)