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 2b4242a3344..e04714ed792 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 @@ -151,8 +151,9 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher if (this.methodNamePatterns.isEmpty()) { return true; } - for (String methodNamePattern : this.methodNamePatterns) { - if (isMatch(element.getMethodName(), methodNamePattern)) { + String methodName = element.getMethodName(); + for (int i = 0; i < this.methodNamePatterns.size(); i++) { + if (isMatch(methodName, i)) { return true; } } @@ -179,8 +180,31 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher this.evaluationCount.incrementAndGet(); } + /** + * Determine if the given method name matches the method name pattern at the + * specified index. + *

This method is invoked by {@link #matches(Method, Class, Object...)}. + *

The default implementation retrieves the method name pattern from + * {@link #methodNamePatterns} and delegates to {@link #isMatch(String, String)}. + *

Can be overridden in subclasses — for example, to support + * regular expressions. + * @param methodName the method name to check + * @param patternIndex the index of the method name pattern + * @return {@code true} if the method name matches the pattern at the specified + * index + * @since 6.1 + * @see #methodNamePatterns + * @see #isMatch(String, String) + * @see #matches(Method, Class, Object...) + */ + protected boolean isMatch(String methodName, int patternIndex) { + String methodNamePattern = this.methodNamePatterns.get(patternIndex); + return isMatch(methodName, methodNamePattern); + } + /** * Determine if the given method name matches the method name pattern. + *

This method is invoked by {@link #isMatch(String, int)}. *

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 — for example, to support a @@ -189,7 +213,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher * @param methodNamePattern the method name pattern * @return {@code true} if the method name matches the pattern * @since 6.1 - * @see #matches(Method, Class, Object...) + * @see #isMatch(String, int) * @see PatternMatchUtils#simpleMatch(String, String) */ protected boolean isMatch(String methodName, String methodNamePattern) { 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 4165fe62a2c..3401b6fa18b 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 @@ -18,6 +18,7 @@ package org.springframework.aop.support; import java.lang.reflect.Method; import java.util.List; +import java.util.regex.Pattern; import org.junit.jupiter.api.Test; @@ -73,6 +74,15 @@ class ControlFlowPointcutTests { assertMatchesSetAndGetAge(cflow); } + @Test + void regExControlFlowPointcut() { + ControlFlowPointcut cflow = new RegExControlFlowPointcut(MyComponent.class, "(set.*?|getAge)"); + assertMatchesSetAndGetAge(cflow); + + cflow = new RegExControlFlowPointcut(MyComponent.class, "set", "^getAge$"); + assertMatchesSetAndGetAge(cflow); + } + @Test void controlFlowPointcutIsExtensible() { CustomControlFlowPointcut cflow = new CustomControlFlowPointcut(MyComponent.class, "set*", "getAge", "set*", "set*"); @@ -249,4 +259,20 @@ class ControlFlowPointcutTests { } } + @SuppressWarnings("serial") + private static class RegExControlFlowPointcut extends ControlFlowPointcut { + + private final List compiledPatterns; + + RegExControlFlowPointcut(Class clazz, String... methodNamePatterns) { + super(clazz, methodNamePatterns); + this.compiledPatterns = super.methodNamePatterns.stream().map(Pattern::compile).toList(); + } + + @Override + protected boolean isMatch(String methodName, int patternIndex) { + return this.compiledPatterns.get(patternIndex).matcher(methodName).matches(); + } + } + }