Browse Source

Select most specific advice method in case of override

Closes gh-32865

(cherry picked from commit ea596aa211)
pull/33211/head
Juergen Hoeller 2 years ago
parent
commit
eff9f5b92a
  1. 27
      spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java
  2. 66
      spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2020 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -50,6 +50,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator; import org.springframework.core.convert.converter.ConvertingComparator;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter; import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -133,17 +134,19 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
List<Advisor> advisors = new ArrayList<>(); List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) { for (Method method : getAdvisorMethods(aspectClass)) {
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
// to getAdvisor(...) to represent the "current position" in the declared methods list. // Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
// However, since Java 7 the "current position" is not valid since the JDK no longer // to getAdvisor(...) to represent the "current position" in the declared methods list.
// returns declared methods in the order in which they are declared in the source code. // However, since Java 7 the "current position" is not valid since the JDK no longer
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods // returns declared methods in the order in which they are declared in the source code.
// discovered via reflection in order to support reliable advice ordering across JVM launches. // Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
// Specifically, a value of 0 aligns with the default value used in // discovered via reflection in order to support reliable advice ordering across JVM launches.
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor). // Specifically, a value of 0 aligns with the default value used in
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); // AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
if (advisor != null) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
advisors.add(advisor); if (advisor != null) {
advisors.add(advisor);
}
} }
} }

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,7 +37,6 @@ import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.DeclarePrecedence; import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature; import org.aspectj.lang.reflect.MethodSignature;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import test.aop.DefaultLockable; import test.aop.DefaultLockable;
import test.aop.Lockable; import test.aop.Lockable;
@ -76,25 +75,24 @@ abstract class AbstractAspectJAdvisorFactoryTests {
/** /**
* To be overridden by concrete test subclasses. * To be overridden by concrete test subclasses.
* @return the fixture
*/ */
protected abstract AspectJAdvisorFactory getFixture(); protected abstract AspectJAdvisorFactory getFixture();
@Test @Test
void rejectsPerCflowAspect() { void rejectsPerCflowAspect() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() -> assertThatExceptionOfType(AopConfigException.class)
getFixture().getAdvisors( .isThrownBy(() -> getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean"))) new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean")))
.withMessageContaining("PERCFLOW"); .withMessageContaining("PERCFLOW");
} }
@Test @Test
void rejectsPerCflowBelowAspect() { void rejectsPerCflowBelowAspect() {
assertThatExceptionOfType(AopConfigException.class).isThrownBy(() -> assertThatExceptionOfType(AopConfigException.class)
getFixture().getAdvisors( .isThrownBy(() -> getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean"))) new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW"); .withMessageContaining("PERCFLOWBELOW");
} }
@Test @Test
@ -385,8 +383,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(lockable.locked()).as("Already locked").isTrue(); assertThat(lockable.locked()).as("Already locked").isTrue();
lockable.lock(); lockable.lock();
assertThat(lockable.locked()).as("Real target ignores locking").isTrue(); assertThat(lockable.locked()).as("Real target ignores locking").isTrue();
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(lockable::unlock);
lockable.unlock());
} }
@Test @Test
@ -413,9 +410,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
lockable.locked(); lockable.locked();
} }
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
@Test @Test
@Disabled
void introductionWithArgumentBinding() { void introductionWithArgumentBinding() {
TestBean target = new TestBean(); TestBean target = new TestBean();
@ -523,6 +518,16 @@ abstract class AbstractAspectJAdvisorFactoryTests {
assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end"); assertThat(aspect.invocations).containsExactly("around - start", "before", "after throwing", "after", "around - end");
} }
@Test
void parentAspect() {
TestBean target = new TestBean("Jane", 42);
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
new IncrementingAspect(), "incrementingAspect");
ITestBean proxy = (ITestBean) createProxy(target,
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
assertThat(proxy.getAge()).isEqualTo(86); // (42 + 1) * 2
}
@Test @Test
void failureWithoutExplicitDeclarePrecedence() { void failureWithoutExplicitDeclarePrecedence() {
TestBean target = new TestBean(); TestBean target = new TestBean();
@ -647,7 +652,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
static class NamedPointcutAspectWithFQN { static class NamedPointcutAspectWithFQN {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean(); private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Pointcut("execution(* getAge())") @Pointcut("execution(* getAge())")
void getAge() { void getAge() {
@ -767,6 +772,31 @@ abstract class AbstractAspectJAdvisorFactoryTests {
} }
@Aspect
abstract static class DoublingAspect {
@Around("execution(* getAge())")
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
}
@Aspect
static class IncrementingAspect extends DoublingAspect {
@Override
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
@Around("execution(* getAge())")
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) + 1;
}
}
@Aspect @Aspect
private static class InvocationTrackingAspect { private static class InvocationTrackingAspect {
@ -824,7 +854,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()") @Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) { int preventExecution(ProceedingJoinPoint pjp) {
return 666; return 42;
} }
} }
@ -844,7 +874,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Around("getAge()") @Around("getAge()")
int preventExecution(ProceedingJoinPoint pjp) { int preventExecution(ProceedingJoinPoint pjp) {
return 666; return 42;
} }
} }
@ -1066,7 +1096,7 @@ class PerThisAspect {
// Just to check that this doesn't cause problems with introduction processing // Just to check that this doesn't cause problems with introduction processing
@SuppressWarnings("unused") @SuppressWarnings("unused")
private ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean(); private final ITestBean fieldThatShouldBeIgnoredBySpringAtAspectJProcessing = new TestBean();
@Around("execution(int *.getAge())") @Around("execution(int *.getAge())")
int returnCountAsAge() { int returnCountAsAge() {

Loading…
Cancel
Save