Spring AOP module providing aspect-oriented programming capabilities for the Spring Framework
—
Various pointcut implementations for different matching strategies including name-based, regular expression, annotation-based, and composable pointcuts. These implementations provide flexible ways to define where advice should be applied in your application, from simple method name patterns to complex AspectJ expressions.
Pointcut implementation that allows combining multiple pointcuts using union and intersection operations.
public class ComposablePointcut implements Pointcut, Serializable {
/**
* Create a default ComposablePointcut, with {@code ClassFilter.TRUE}
* and {@code MethodMatcher.TRUE}.
*/
public ComposablePointcut();
/**
* Create a ComposablePointcut based on the given Pointcut.
* @param pointcut the original Pointcut
*/
public ComposablePointcut(Pointcut pointcut);
/**
* Create a ComposablePointcut for the given ClassFilter,
* with {@code MethodMatcher.TRUE}.
* @param classFilter the ClassFilter to use
*/
public ComposablePointcut(ClassFilter classFilter);
/**
* Create a ComposablePointcut for the given MethodMatcher,
* with {@code ClassFilter.TRUE}.
* @param methodMatcher the MethodMatcher to use
*/
public ComposablePointcut(MethodMatcher methodMatcher);
/**
* Create a ComposablePointcut for the given ClassFilter and MethodMatcher.
* @param classFilter the ClassFilter to use
* @param methodMatcher the MethodMatcher to use
*/
public ComposablePointcut(ClassFilter classFilter, MethodMatcher methodMatcher);
/**
* Apply a union with the given Pointcut.
* @param other the Pointcut to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(Pointcut other);
/**
* Apply an intersection with the given Pointcut.
* @param other the Pointcut to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(Pointcut other);
/**
* Apply a union with the given ClassFilter.
* @param other the ClassFilter to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(ClassFilter other);
/**
* Apply an intersection with the given ClassFilter.
* @param other the ClassFilter to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(ClassFilter other);
/**
* Apply a union with the given MethodMatcher.
* @param other the MethodMatcher to apply a union with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut union(MethodMatcher other);
/**
* Apply an intersection with the given MethodMatcher.
* @param other the MethodMatcher to apply an intersection with
* @return this composable pointcut (for call chaining)
*/
public ComposablePointcut intersection(MethodMatcher other);
@Override
public ClassFilter getClassFilter();
@Override
public MethodMatcher getMethodMatcher();
}Pointcut implementations that match methods based on method names or patterns.
public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
/**
* Convenience method when we have only a single method name to match.
* Use either this method or {@code setMappedNames}, not both.
* @param mappedName the name of the method to match
* @see #setMappedNames
*/
public void setMappedName(String mappedName);
/**
* Set the method names defining methods to match.
* Matching will be the union of all these; if any match, the pointcut matches.
* @param mappedNames the method names to match
*/
public void setMappedNames(String... mappedNames);
/**
* Add another eligible method name, in addition to those already named.
* Like the set methods, this method is for use when configuring proxies,
* before a proxy is used.
* <p><b>NB:</b> This method does not work after the proxy is in
* use, as advice chains will be cached.
* @param name name of the additional method that will match
* @return this pointcut to allow for multiple additions in one line
*/
public NameMatchMethodPointcut addMethodName(String name);
@Override
public boolean matches(Method method, Class<?> targetClass);
@Override
public boolean equals(Object other);
@Override
public int hashCode();
}Pointcut implementations using regular expressions for method matching.
public abstract class AbstractRegexpMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
/**
* Convenience method when we have only a single pattern.
* Use either this method or {@code setPatterns}, not both.
* @param pattern the pattern to match
* @see #setPatterns
*/
public void setPattern(String pattern);
/**
* Set the regular expressions defining methods to match.
* Matching will be the union of all these; if any match, the pointcut matches.
* @param patterns the patterns to match
*/
public void setPatterns(String... patterns);
/**
* Return the regular expressions for method matching.
*/
public String[] getPatterns();
/**
* Try to match the regular expression against the fully qualified name
* of the target class as well as against the method's declaring class.
*/
@Override
public boolean matches(Method method, Class<?> targetClass);
/**
* Subclasses must implement this to initialize regex pointcuts.
* Can be invoked multiple times.
* <p>This method will be invoked from the {@code setPatterns} method,
* and also on deserialization.
* @param patterns the patterns to initialize
* @throws IllegalArgumentException in case of an invalid pattern
*/
protected abstract void initPatternRepresentation(String[] patterns) throws IllegalArgumentException;
/**
* Does the pattern at the given index match the given String?
* @param patternIndex the index of the pattern to match against
* @param str the String to match
* @return whether there's a match
*/
protected abstract boolean matches(String pattern, int patternIndex);
}
public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {
/**
* Internal method to initialize the {@code Pattern} representation of the pointcut.
*/
@Override
protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException;
/**
* Does the pattern at the given index match this string?
*/
@Override
protected boolean matches(String pattern, int patternIndex);
}Pointcut that matches methods executing under a specific control flow.
public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
/**
* Construct a new pointcut that matches all methods executing
* under a class with the given name, in any package.
* @param clazz the clazz
*/
public ControlFlowPointcut(Class<?> clazz);
/**
* Construct a new pointcut that matches all methods executing
* under a class with the given name, in any package.
* @param clazz the clazz
* @param methodName the name of the method (may be {@code null})
*/
public ControlFlowPointcut(Class<?> clazz, String methodName);
/**
* Subclasses can override this for greater filtering (and performance).
*/
@Override
public boolean matches(Class<?> clazz);
/**
* Subclasses can override this if it's possible to filter out
* some candidate classes.
*/
@Override
public boolean matches(Method method, Class<?> targetClass);
@Override
public boolean isRuntime();
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args);
@Override
public ClassFilter getClassFilter();
@Override
public MethodMatcher getMethodMatcher();
}Pointcut implementations that match based on annotations present on classes or methods.
public class AnnotationMatchingPointcut implements Pointcut {
/**
* Create a new AnnotationMatchingPointcut for which both the class and method
* annotations will be checked.
* @param classAnnotationType the annotation type to check at the class level
* (can be {@code null})
* @param methodAnnotationType the annotation type to check at the method level
* (can be {@code null})
*/
public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType,
Class<? extends Annotation> methodAnnotationType);
/**
* Create a new AnnotationMatchingPointcut for the given annotation type.
* @param classAnnotationType the annotation type to check at the class level
* @param checkInherited whether to explicitly check the superclasses and
* interfaces for the annotation type as well (even if the annotation type
* is not marked as inherited itself)
*/
public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited);
/**
* Factory method for an AnnotationMatchingPointcut that matches
* all methods carrying the specified annotation at the class level.
* @param annotationType the annotation type to check at the class level
* @return the corresponding AnnotationMatchingPointcut
*/
public static AnnotationMatchingPointcut forClassAnnotation(Class<? extends Annotation> annotationType);
/**
* Factory method for an AnnotationMatchingPointcut that matches
* all methods carrying the specified annotation at the method level.
* @param annotationType the annotation type to check at the method level
* @return the corresponding AnnotationMatchingPointcut
*/
public static AnnotationMatchingPointcut forMethodAnnotation(Class<? extends Annotation> annotationType);
@Override
public ClassFilter getClassFilter();
@Override
public MethodMatcher getMethodMatcher();
}
public class AnnotationClassFilter implements ClassFilter {
/**
* Create a new AnnotationClassFilter for the given annotation type.
* @param annotationType the annotation type to look for
*/
public AnnotationClassFilter(Class<? extends Annotation> annotationType);
/**
* Create a new AnnotationClassFilter for the given annotation type.
* @param annotationType the annotation type to look for
* @param checkInherited whether to explicitly check the superclasses and
* interfaces for the annotation type as well (even if the annotation type
* is not marked as inherited itself)
*/
public AnnotationClassFilter(Class<? extends Annotation> annotationType, boolean checkInherited);
@Override
public boolean matches(Class<?> clazz);
}
public class AnnotationMethodMatcher extends StaticMethodMatcher {
/**
* Create a new AnnotationMethodMatcher for the given annotation type.
* @param annotationType the annotation type to look for
*/
public AnnotationMethodMatcher(Class<? extends Annotation> annotationType);
/**
* Create a new AnnotationMethodMatcher for the given annotation type.
* @param annotationType the annotation type to look for
* @param checkInherited whether to explicitly check the superclasses and
* interfaces for the annotation type as well (even if the annotation type
* is not marked as inherited itself)
*/
public AnnotationMethodMatcher(Class<? extends Annotation> annotationType, boolean checkInherited);
@Override
public boolean matches(Method method, Class<?> targetClass);
}Base classes for implementing static method matchers (no runtime checks needed).
public abstract class StaticMethodMatcher implements MethodMatcher {
@Override
public final boolean isRuntime();
@Override
public final boolean matches(Method method, Class<?> targetClass, Object... args);
/**
* Subclasses must implement this to return {@code true} if the method
* matches. Note that there will be no runtime check (i.e. no check on the
* method's arguments) if this method returns {@code false}.
* @param method the candidate method
* @param targetClass target class (may be {@code null}, in which case
* the candidate class must be taken to be the method's declaring class)
* @return whether or not this method matches statically
*/
public abstract boolean matches(Method method, Class<?> targetClass);
}
public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter);
@Override
public ClassFilter getClassFilter();
@Override
public final MethodMatcher getMethodMatcher();
}
public abstract class DynamicMethodMatcher implements MethodMatcher {
@Override
public final boolean isRuntime();
/**
* Can override to add preconditions for dynamic matching. This implementation
* always returns true.
*/
@Override
public boolean matches(Method method, Class<?> targetClass);
/**
* Must override to implement runtime matching.
* The result of the 2-parameter {@link #matches(Method, Class)} method
* is taken into account for static matching checks already.
* @param method the candidate method
* @param targetClass the target class
* @param args arguments to the method
* @return whether there's a runtime match
*/
public abstract boolean matches(Method method, Class<?> targetClass, Object... args);
}
public abstract class DynamicMethodMatcherPointcut extends DynamicMethodMatcher implements Pointcut {
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter);
@Override
public ClassFilter getClassFilter();
@Override
public final MethodMatcher getMethodMatcher();
}Utility classes providing static methods for working with pointcuts, class filters, and method matchers.
public abstract class Pointcuts {
/**
* Match all methods that <i>either</i> (or both) of the given pointcuts matches.
* @param pc1 the first Pointcut
* @param pc2 the second Pointcut
* @return a distinct Pointcut that matches all methods that either
* of the given Pointcuts matches
*/
public static Pointcut union(Pointcut pc1, Pointcut pc2);
/**
* Match all methods that <i>both</i> the given pointcuts match.
* @param pc1 the first Pointcut
* @param pc2 the second Pointcut
* @return a distinct Pointcut that matches all methods that both
* of the given Pointcuts match
*/
public static Pointcut intersection(Pointcut pc1, Pointcut pc2);
/**
* Perform the least expensive check for a pointcut match.
* @param pointcut the pointcut to match
* @param method the candidate method
* @param targetClass the target class
* @param args arguments to the method
* @return whether there's a runtime match
*/
public static boolean matches(Pointcut pointcut, Method method, Class<?> targetClass, Object... args);
}
public abstract class ClassFilters {
/**
* Match all classes that <i>either</i> (or both) of the given ClassFilters matches.
* @param cf1 the first ClassFilter
* @param cf2 the second ClassFilter
* @return a distinct ClassFilter that matches all classes that either
* of the given ClassFilter matches
*/
public static ClassFilter union(ClassFilter cf1, ClassFilter cf2);
/**
* Match all classes that <i>both</i> the given ClassFilters match.
* @param cf1 the first ClassFilter
* @param cf2 the second ClassFilter
* @return a distinct ClassFilter that matches all classes that both
* of the given ClassFilters match
*/
public static ClassFilter intersection(ClassFilter cf1, ClassFilter cf2);
}
public abstract class MethodMatchers {
/**
* Match all methods that <i>either</i> (or both) of the given MethodMatchers matches.
* @param mm1 the first MethodMatcher
* @param mm2 the second MethodMatcher
* @return a distinct MethodMatcher that matches all methods that either
* of the given MethodMatchers matches
*/
public static MethodMatcher union(MethodMatcher mm1, MethodMatcher mm2);
/**
* Match all methods that <i>both</i> the given MethodMatchers match.
* @param mm1 the first MethodMatcher
* @param mm2 the second MethodMatcher
* @return a distinct MethodMatcher that matches all methods that both
* of the given MethodMatchers match
*/
public static MethodMatcher intersection(MethodMatcher mm1, MethodMatcher mm2);
}Interface for pointcuts that use string expressions for definition.
public interface ExpressionPointcut {
/**
* Return the String expression for this pointcut.
* @return the String expression
*/
String getExpression();
}// Start with a basic name-based pointcut
NameMatchMethodPointcut namePointcut = new NameMatchMethodPointcut();
namePointcut.setMappedNames("save*", "update*", "delete*");
// Create annotation-based pointcut
AnnotationMatchingPointcut annotationPointcut =
AnnotationMatchingPointcut.forMethodAnnotation(Transactional.class);
// Combine them using union
ComposablePointcut composablePointcut = new ComposablePointcut(namePointcut)
.union(annotationPointcut);
// Add class filter to restrict to service classes
ClassFilter serviceFilter = new AnnotationClassFilter(Service.class);
composablePointcut.intersection(serviceFilter);
// Create advisor with composed pointcut
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(
composablePointcut,
new LoggingInterceptor()
);// Create regex pointcut for service layer methods
JdkRegexpMethodPointcut regexPointcut = new JdkRegexpMethodPointcut();
regexPointcut.setPatterns(
".*\\.service\\..*Service\\..*",
".*\\.repository\\..*Repository\\.find.*",
".*\\.dao\\..*Dao\\.get.*"
);
// Use in advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(
regexPointcut,
new PerformanceMonitorInterceptor()
);// Match methods called from specific control flow
ControlFlowPointcut controlFlowPointcut = new ControlFlowPointcut(
UserController.class,
"handleRequest"
);
// All methods called during UserController.handleRequest() execution
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(
controlFlowPointcut,
new SecurityInterceptor()
);// Custom dynamic matcher based on method parameters
DynamicMethodMatcher dynamicMatcher = new DynamicMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// Static check: method must have parameters
return method.getParameterCount() > 0;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
// Dynamic check: first parameter must be non-null String
return args.length > 0 &&
args[0] instanceof String &&
args[0] != null;
}
};
// Convert to pointcut
DynamicMethodMatcherPointcut dynamicPointcut = new DynamicMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return dynamicMatcher.matches(method, targetClass);
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return dynamicMatcher.matches(method, targetClass, args);
}
};// Match methods with specific return type
public class ReturnTypeMethodMatcher extends StaticMethodMatcher {
private final Class<?> expectedReturnType;
public ReturnTypeMethodMatcher(Class<?> expectedReturnType) {
this.expectedReturnType = expectedReturnType;
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
return expectedReturnType.isAssignableFrom(method.getReturnType());
}
}
// Use the custom matcher
ReturnTypeMethodMatcher stringReturnMatcher = new ReturnTypeMethodMatcher(String.class);
StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return stringReturnMatcher.matches(method, targetClass);
}
};// Create complex pointcut using utility methods
ClassFilter serviceOrController = ClassFilters.union(
new AnnotationClassFilter(Service.class),
new AnnotationClassFilter(Controller.class)
);
MethodMatcher publicMethods = new MethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return Modifier.isPublic(method.getModifiers());
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
throw new UnsupportedOperationException();
}
};
MethodMatcher nonGetterMethods = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return !method.getName().startsWith("get") &&
!method.getName().startsWith("is");
}
};
MethodMatcher combinedMethodMatcher = MethodMatchers.intersection(
publicMethods,
nonGetterMethods
);
// Create final pointcut
ComposablePointcut finalPointcut = new ComposablePointcut(
serviceOrController,
combinedMethodMatcher
);Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-aop