Browse Source

Polishing

pull/31518/head
Sam Brannen 2 years ago
parent
commit
7f1beb0140
  1. 6
      spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java
  2. 148
      spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java

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

@ -44,7 +44,6 @@ import org.springframework.util.PatternMatchUtils;
* @author Rob Harrop * @author Rob Harrop
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @see #isMatch
* @see NameMatchMethodPointcut * @see NameMatchMethodPointcut
* @see JdkRegexpMethodPointcut * @see JdkRegexpMethodPointcut
*/ */
@ -58,7 +57,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
protected final Class<?> clazz; protected final Class<?> clazz;
/** /**
* An immutable list of method name patterns against which to match. * An immutable list of distinct method name patterns against which to match.
* @since 6.1 * @since 6.1
*/ */
protected final List<String> methodNamePatterns; protected final List<String> methodNamePatterns;
@ -184,7 +183,8 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
* Determine if the given method name matches the method name pattern. * Determine if the given method name matches the method name pattern.
* <p>The default implementation checks for direct equality as well as * <p>The default implementation checks for direct equality as well as
* {@code xxx*}, {@code *xxx}, {@code *xxx*}, and {@code xxx*yyy} matches. * {@code xxx*}, {@code *xxx}, {@code *xxx*}, and {@code xxx*yyy} matches.
* <p>Can be overridden in subclasses. * <p>Can be overridden in subclasses &mdash; for example, to support a
* different style of simple pattern matching.
* @param methodName the method name to check * @param methodName the method name to check
* @param methodNamePattern the method name pattern * @param methodNamePattern the method name pattern
* @return {@code true} if the method name matches the pattern * @return {@code true} if the method name matches the pattern

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

@ -50,102 +50,35 @@ class ControlFlowPointcutTests {
// Will not be advised: not under MyComponent // Will not be advised: not under MyComponent
assertThat(proxy.getAge()).isEqualTo(target.getAge()); assertThat(proxy.getAge()).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(1); assertThat(cflow.getEvaluations()).isEqualTo(1);
assertThat(nop.getCount()).isEqualTo(0);
// Will be advised due to "getAge" pattern: the proxy is invoked under MyComponent#getAge // Will be advised due to "getAge" pattern: the proxy is invoked under MyComponent#getAge
assertThat(component.getAge(proxy)).isEqualTo(target.getAge()); assertThat(component.getAge(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(2); assertThat(cflow.getEvaluations()).isEqualTo(2);
assertThat(nop.getCount()).isEqualTo(1);
// Will not be advised: the proxy is invoked under MyComponent, but there is no match for "nomatch" // Will not be advised: the proxy is invoked under MyComponent, but there is no match for "nomatch"
assertThat(component.nomatch(proxy)).isEqualTo(target.getAge()); assertThat(component.nomatch(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(3); assertThat(cflow.getEvaluations()).isEqualTo(3);
assertThat(nop.getCount()).isEqualTo(1);
} }
@Test @Test
void matchesMethodNamePatterns() { void matchesMethodNamePatterns() {
MyComponent component = new MyComponent(); ControlFlowPointcut cflow = pointcut("set", "getAge");
TestBean target = new TestBean("Jane", 27); assertMatchesSetAndGetAge(cflow);
ControlFlowPointcut cflow = pointcut("foo", "get*", "bar", "*se*", "baz");
NopInterceptor nop = new NopInterceptor();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvisor(new DefaultPointcutAdvisor(cflow, nop));
ITestBean proxy = (ITestBean) pf.getProxy();
// Will not be advised: not under MyComponent
assertThat(proxy.getAge()).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(1);
// Will be advised due to "get*" pattern: the proxy is invoked under MyComponent#getAge
assertThat(component.getAge(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(2);
// Will be advised due to "*se*" pattern: the proxy is invoked under MyComponent#set
component.set(proxy);
assertThat(proxy.getAge()).isEqualTo(5);
assertThat(nop.getCount()).isEqualTo(2);
assertThat(cflow.getEvaluations()).isEqualTo(4);
// Will not be advised: the proxy is invoked under MyComponent, but there is no match for "nomatch" cflow = pointcut("foo", "get*", "bar", "*se*", "baz");
assertThat(component.nomatch(proxy)).isEqualTo(target.getAge()); assertMatchesSetAndGetAge(cflow);
assertThat(nop.getCount()).isEqualTo(2);
assertThat(cflow.getEvaluations()).isEqualTo(5);
} }
@Test @Test
void controlFlowPointcutIsExtensible() { void controlFlowPointcutIsExtensible() {
@SuppressWarnings("serial")
class CustomControlFlowPointcut extends ControlFlowPointcut {
CustomControlFlowPointcut(Class<?> clazz, String... methodNamePatterns) {
super(clazz, methodNamePatterns);
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
super.incrementEvaluationCount();
return super.matches(method, targetClass, args);
}
Class<?> trackedClass() {
return super.clazz;
}
List<String> trackedMethodNamePatterns() {
return super.methodNamePatterns;
}
}
CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(MyComponent.class, "set*", "getAge", "set*", "set*"); CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(MyComponent.class, "set*", "getAge", "set*", "set*");
assertMatchesSetAndGetAge(cflow, 2);
assertThat(cflow.trackedClass()).isEqualTo(MyComponent.class); assertThat(cflow.trackedClass()).isEqualTo(MyComponent.class);
assertThat(cflow.trackedMethodNamePatterns()).containsExactly("set*", "getAge"); assertThat(cflow.trackedMethodNamePatterns()).containsExactly("set*", "getAge");
MyComponent component = new MyComponent();
TestBean target = new TestBean("Jane", 27);
NopInterceptor nop = new NopInterceptor();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvisor(new DefaultPointcutAdvisor(cflow, nop));
ITestBean proxy = (ITestBean) pf.getProxy();
// Will not be advised: the proxy is not invoked under MyComponent#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 MyComponent#getAge
assertThat(component.getAge(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(4); // intentional double increment
// Will not be advised: the proxy is invoked under MyComponent, but there is no match for "nomatch"
assertThat(component.nomatch(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(6); // intentional double increment
} }
/** /**
@ -156,25 +89,25 @@ class ControlFlowPointcutTests {
* expensive. * expensive.
*/ */
@Test @Test
void selectiveApplication() { void controlFlowPointcutCanBeCombinedWithStaticPointcut() {
MyComponent component = new MyComponent(); MyComponent component = new MyComponent();
TestBean target = new TestBean("Jane", 27); TestBean target = new TestBean("Jane", 27);
ControlFlowPointcut cflow = pointcut(); ControlFlowPointcut cflow = pointcut();
NopInterceptor nop = new NopInterceptor();
Pointcut settersUnderMyComponent = Pointcuts.intersection(Pointcuts.SETTERS, cflow); Pointcut settersUnderMyComponent = Pointcuts.intersection(Pointcuts.SETTERS, cflow);
NopInterceptor nop = new NopInterceptor();
ProxyFactory pf = new ProxyFactory(target); ProxyFactory pf = new ProxyFactory(target);
pf.addAdvisor(new DefaultPointcutAdvisor(settersUnderMyComponent, nop)); pf.addAdvisor(new DefaultPointcutAdvisor(settersUnderMyComponent, nop));
ITestBean proxy = (ITestBean) pf.getProxy(); ITestBean proxy = (ITestBean) pf.getProxy();
// Will not be advised: not under MyComponent // Will not be advised: not under MyComponent
target.setAge(16); target.setAge(16);
assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(0); assertThat(cflow.getEvaluations()).isEqualTo(0);
assertThat(nop.getCount()).isEqualTo(0);
// Will not be advised: under MyComponent but not a setter // Will not be advised: under MyComponent but not a setter
assertThat(component.getAge(proxy)).isEqualTo(16); assertThat(component.getAge(proxy)).isEqualTo(16);
assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(0); assertThat(cflow.getEvaluations()).isEqualTo(0);
assertThat(nop.getCount()).isEqualTo(0);
// Will be advised due to Pointcuts.SETTERS: the proxy is invoked under MyComponent#set // Will be advised due to Pointcuts.SETTERS: the proxy is invoked under MyComponent#set
component.set(proxy); component.set(proxy);
@ -246,6 +179,41 @@ class ControlFlowPointcutTests {
return new ControlFlowPointcut(MyComponent.class, methodNamePatterns); return new ControlFlowPointcut(MyComponent.class, methodNamePatterns);
} }
private static void assertMatchesSetAndGetAge(ControlFlowPointcut cflow) {
assertMatchesSetAndGetAge(cflow, 1);
}
private static void assertMatchesSetAndGetAge(ControlFlowPointcut cflow, int evaluationFactor) {
MyComponent component = new MyComponent();
TestBean target = new TestBean("Jane", 27);
NopInterceptor nop = new NopInterceptor();
ProxyFactory pf = new ProxyFactory(target);
pf.addAdvisor(new DefaultPointcutAdvisor(cflow, nop));
ITestBean proxy = (ITestBean) pf.getProxy();
// Will not be advised: not under MyComponent
assertThat(proxy.getAge()).isEqualTo(target.getAge());
assertThat(cflow.getEvaluations()).isEqualTo(1 * evaluationFactor);
assertThat(nop.getCount()).isEqualTo(0);
// Will be advised: the proxy is invoked under MyComponent#getAge
assertThat(component.getAge(proxy)).isEqualTo(target.getAge());
assertThat(cflow.getEvaluations()).isEqualTo(2 * evaluationFactor);
assertThat(nop.getCount()).isEqualTo(1);
// Will be advised: the proxy is invoked under MyComponent#set
component.set(proxy);
assertThat(cflow.getEvaluations()).isEqualTo(3 * evaluationFactor);
assertThat(proxy.getAge()).isEqualTo(5);
assertThat(cflow.getEvaluations()).isEqualTo(4 * evaluationFactor);
assertThat(nop.getCount()).isEqualTo(2);
// Will not be advised: the proxy is invoked under MyComponent, but there is no match for "nomatch"
assertThat(component.nomatch(proxy)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(2);
assertThat(cflow.getEvaluations()).isEqualTo(5 * evaluationFactor);
}
private static class MyComponent { private static class MyComponent {
int getAge(ITestBean proxy) { int getAge(ITestBean proxy) {
@ -259,4 +227,26 @@ class ControlFlowPointcutTests {
} }
} }
@SuppressWarnings("serial")
private static class CustomControlFlowPointcut extends ControlFlowPointcut {
CustomControlFlowPointcut(Class<?> clazz, String... methodNamePatterns) {
super(clazz, methodNamePatterns);
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
super.incrementEvaluationCount();
return super.matches(method, targetClass, args);
}
Class<?> trackedClass() {
return super.clazz;
}
List<String> trackedMethodNamePatterns() {
return super.methodNamePatterns;
}
}
} }

Loading…
Cancel
Save