From 7f1beb01400913cfbe3309c389ba7cef6a1dbe22 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 28 Oct 2023 15:05:26 +0200 Subject: [PATCH] Polishing --- .../aop/support/ControlFlowPointcut.java | 6 +- .../aop/support/ControlFlowPointcutTests.java | 148 ++++++++---------- 2 files changed, 72 insertions(+), 82 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java index 7f56a9c81f0..2b4242a3344 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java @@ -44,7 +44,6 @@ import org.springframework.util.PatternMatchUtils; * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen - * @see #isMatch * @see NameMatchMethodPointcut * @see JdkRegexpMethodPointcut */ @@ -58,7 +57,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher 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 */ protected final List methodNamePatterns; @@ -184,7 +183,8 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher * Determine if the given method name matches the method name pattern. *

The default implementation checks for direct equality as well as * {@code xxx*}, {@code *xxx}, {@code *xxx*}, and {@code xxx*yyy} matches. - *

Can be overridden in subclasses. + *

Can be overridden in subclasses — for example, to support a + * different style of simple pattern matching. * @param methodName the method name to check * @param methodNamePattern the method name pattern * @return {@code true} if the method name matches the pattern diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java index d9cb49c1a21..4165fe62a2c 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ControlFlowPointcutTests.java @@ -50,102 +50,35 @@ class ControlFlowPointcutTests { // Will not be advised: not under MyComponent assertThat(proxy.getAge()).isEqualTo(target.getAge()); - assertThat(nop.getCount()).isEqualTo(0); assertThat(cflow.getEvaluations()).isEqualTo(1); + assertThat(nop.getCount()).isEqualTo(0); // Will be advised due to "getAge" 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); + assertThat(nop.getCount()).isEqualTo(1); // 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(3); + assertThat(nop.getCount()).isEqualTo(1); } @Test void matchesMethodNamePatterns() { - MyComponent component = new MyComponent(); - TestBean target = new TestBean("Jane", 27); - 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(); + ControlFlowPointcut cflow = pointcut("set", "getAge"); + assertMatchesSetAndGetAge(cflow); - // 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" - assertThat(component.nomatch(proxy)).isEqualTo(target.getAge()); - assertThat(nop.getCount()).isEqualTo(2); - assertThat(cflow.getEvaluations()).isEqualTo(5); + cflow = pointcut("foo", "get*", "bar", "*se*", "baz"); + assertMatchesSetAndGetAge(cflow); } @Test 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 trackedMethodNamePatterns() { - return super.methodNamePatterns; - } - } - CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(MyComponent.class, "set*", "getAge", "set*", "set*"); - + assertMatchesSetAndGetAge(cflow, 2); assertThat(cflow.trackedClass()).isEqualTo(MyComponent.class); 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. */ @Test - void selectiveApplication() { + void controlFlowPointcutCanBeCombinedWithStaticPointcut() { MyComponent component = new MyComponent(); TestBean target = new TestBean("Jane", 27); ControlFlowPointcut cflow = pointcut(); - NopInterceptor nop = new NopInterceptor(); Pointcut settersUnderMyComponent = Pointcuts.intersection(Pointcuts.SETTERS, cflow); + NopInterceptor nop = new NopInterceptor(); ProxyFactory pf = new ProxyFactory(target); pf.addAdvisor(new DefaultPointcutAdvisor(settersUnderMyComponent, nop)); ITestBean proxy = (ITestBean) pf.getProxy(); // Will not be advised: not under MyComponent target.setAge(16); - assertThat(nop.getCount()).isEqualTo(0); assertThat(cflow.getEvaluations()).isEqualTo(0); + assertThat(nop.getCount()).isEqualTo(0); // Will not be advised: under MyComponent but not a setter assertThat(component.getAge(proxy)).isEqualTo(16); - assertThat(nop.getCount()).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 component.set(proxy); @@ -246,6 +179,41 @@ class ControlFlowPointcutTests { 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 { 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 trackedMethodNamePatterns() { + return super.methodNamePatterns; + } + } + }