Skip to content

Commit 5c1cdcb

Browse files
committed
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
1 parent c0f79ca commit 5c1cdcb

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,18 @@
4141
@SuppressWarnings("serial")
4242
public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
4343

44-
private final Class<?> clazz;
44+
/**
45+
* The class against which to match.
46+
* <p>Available for use in subclasses since 6.1.
47+
*/
48+
protected final Class<?> clazz;
4549

50+
/**
51+
* The method against which to match, potentially {@code null}.
52+
* <p>Available for use in subclasses since 6.1.
53+
*/
4654
@Nullable
47-
private final String methodName;
55+
protected final String methodName;
4856

4957
private final AtomicInteger evaluationCount = new AtomicInteger();
5058

@@ -97,7 +105,7 @@ public boolean isRuntime() {
97105

98106
@Override
99107
public boolean matches(Method method, Class<?> targetClass, Object... args) {
100-
this.evaluationCount.incrementAndGet();
108+
incrementEvaluationCount();
101109

102110
for (StackTraceElement element : new Throwable().getStackTrace()) {
103111
if (element.getClassName().equals(this.clazz.getName()) &&
@@ -117,6 +125,15 @@ public int getEvaluations() {
117125
return this.evaluationCount.get();
118126
}
119127

128+
/**
129+
* Increment the {@link #getEvaluations() evaluation count}.
130+
* @since 6.1
131+
* @see #matches(Method, Class, Object...)
132+
*/
133+
protected final void incrementEvaluationCount() {
134+
this.evaluationCount.incrementAndGet();
135+
}
136+
120137

121138
@Override
122139
public ClassFilter getClassFilter() {

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.aop.support;
1818

19+
import java.lang.reflect.Method;
20+
1921
import org.junit.jupiter.api.Test;
2022

2123
import org.springframework.aop.Pointcut;
@@ -31,6 +33,7 @@
3133
*
3234
* @author Rod Johnson
3335
* @author Chris Beams
36+
* @author Sam Brannen
3437
*/
3538
class ControlFlowPointcutTests {
3639

@@ -60,6 +63,57 @@ void matches() {
6063
assertThat(cflow.getEvaluations()).isEqualTo(3);
6164
}
6265

66+
@Test
67+
void controlFlowPointcutIsExtensible() {
68+
@SuppressWarnings("serial")
69+
class CustomControlFlowPointcut extends ControlFlowPointcut {
70+
71+
CustomControlFlowPointcut(Class<?> clazz, String methodName) {
72+
super(clazz, methodName);
73+
}
74+
75+
@Override
76+
public boolean matches(Method method, Class<?> targetClass, Object... args) {
77+
super.incrementEvaluationCount();
78+
return super.matches(method, targetClass, args);
79+
}
80+
81+
Class<?> trackedClass() {
82+
return super.clazz;
83+
}
84+
85+
String trackedMethod() {
86+
return super.methodName;
87+
}
88+
}
89+
90+
CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(One.class, "getAge");
91+
92+
assertThat(cflow.trackedClass()).isEqualTo(One.class);
93+
assertThat(cflow.trackedMethod()).isEqualTo("getAge");
94+
95+
TestBean target = new TestBean("Jane", 27);
96+
ProxyFactory pf = new ProxyFactory(target);
97+
NopInterceptor nop = new NopInterceptor();
98+
pf.addAdvisor(new DefaultPointcutAdvisor(cflow, nop));
99+
ITestBean proxy = (ITestBean) pf.getProxy();
100+
101+
// Not advised: the proxy is not invoked under One#getAge
102+
assertThat(proxy.getAge()).isEqualTo(target.getAge());
103+
assertThat(nop.getCount()).isEqualTo(0);
104+
assertThat(cflow.getEvaluations()).isEqualTo(2); // intentional double increment
105+
106+
// Will be advised: the proxy is invoked under One#getAge
107+
assertThat(new One().getAge(proxy)).isEqualTo(target.getAge());
108+
assertThat(nop.getCount()).isEqualTo(1);
109+
assertThat(cflow.getEvaluations()).isEqualTo(4); // intentional double increment
110+
111+
// Won't be advised: the proxy is not invoked under One#getAge
112+
assertThat(new One().nomatch(proxy)).isEqualTo(target.getAge());
113+
assertThat(nop.getCount()).isEqualTo(1);
114+
assertThat(cflow.getEvaluations()).isEqualTo(6); // intentional double increment
115+
}
116+
63117
/**
64118
* Check that we can use a cflow pointcut only in conjunction with
65119
* a static pointcut: e.g. all setter methods that are invoked under

0 commit comments

Comments
 (0)