Browse Source

Improve ControlFlowPointcut extensibility

This commit makes ControlFlowPointcut more open to subclasses by:

1. Making the ControlFlowPointcut#clazz field protected.
2. Making the ControlFlowPointcut#methodName field protected.
3. Introducing a protected incrementEvaluationCount() method.

Closes gh-27187
pull/31518/head
Sam Brannen 2 years ago
parent
commit
5c1cdcb245
  1. 23
      spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java
  2. 54
      spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java

23
spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java

@ -41,10 +41,18 @@ import org.springframework.util.ObjectUtils; @@ -41,10 +41,18 @@ import org.springframework.util.ObjectUtils;
@SuppressWarnings("serial")
public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
private final Class<?> clazz;
/**
* The class against which to match.
* <p>Available for use in subclasses since 6.1.
*/
protected final Class<?> clazz;
/**
* The method against which to match, potentially {@code null}.
* <p>Available for use in subclasses since 6.1.
*/
@Nullable
private final String methodName;
protected final String methodName;
private final AtomicInteger evaluationCount = new AtomicInteger();
@ -97,7 +105,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher @@ -97,7 +105,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
this.evaluationCount.incrementAndGet();
incrementEvaluationCount();
for (StackTraceElement element : new Throwable().getStackTrace()) {
if (element.getClassName().equals(this.clazz.getName()) &&
@ -117,6 +125,15 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher @@ -117,6 +125,15 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
return this.evaluationCount.get();
}
/**
* Increment the {@link #getEvaluations() evaluation count}.
* @since 6.1
* @see #matches(Method, Class, Object...)
*/
protected final void incrementEvaluationCount() {
this.evaluationCount.incrementAndGet();
}
@Override
public ClassFilter getClassFilter() {

54
spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.aop.support;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.aop.Pointcut;
@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Rod Johnson
* @author Chris Beams
* @author Sam Brannen
*/
class ControlFlowPointcutTests {
@ -60,6 +63,57 @@ class ControlFlowPointcutTests { @@ -60,6 +63,57 @@ class ControlFlowPointcutTests {
assertThat(cflow.getEvaluations()).isEqualTo(3);
}
@Test
void controlFlowPointcutIsExtensible() {
@SuppressWarnings("serial")
class CustomControlFlowPointcut extends ControlFlowPointcut {
CustomControlFlowPointcut(Class<?> clazz, String methodName) {
super(clazz, methodName);
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
super.incrementEvaluationCount();
return super.matches(method, targetClass, args);
}
Class<?> trackedClass() {
return super.clazz;
}
String trackedMethod() {
return super.methodName;
}
}
CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(One.class, "getAge");
assertThat(cflow.trackedClass()).isEqualTo(One.class);
assertThat(cflow.trackedMethod()).isEqualTo("getAge");
TestBean target = new TestBean("Jane", 27);
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
pf.addAdvisor(new DefaultPointcutAdvisor(cflow, nop));
ITestBean proxy = (ITestBean) pf.getProxy();
// Not advised: the proxy is not invoked under One#getAge
assertThat(proxy.getAge()).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(2); // intentional double increment
// Will be advised: the proxy is invoked under One#getAge
assertThat(new One().getAge(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(4); // intentional double increment
// Won't be advised: the proxy is not invoked under One#getAge
assertThat(new One().nomatch(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(6); // intentional double increment
}
/**
* Check that we can use a cflow pointcut only in conjunction with
* a static pointcut: e.g. all setter methods that are invoked under

Loading…
Cancel
Save