Spring AOP module providing aspect-oriented programming capabilities for the Spring Framework
—
Fundamental interfaces and classes that form the foundation of Spring's AOP system. These abstractions provide the basic building blocks for implementing aspect-oriented programming, including pointcuts for defining where advice should be applied, advisors that combine advice with pointcuts, and various advice types for different interception strategies.
Core abstraction for defining where advice should be applied, combining class filters and method matchers to precisely target join points.
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never null)
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never null)
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
public interface ClassFilter {
/**
* Should the pointcut apply to the given interface or target class?
* @param clazz the candidate target class
* @return whether the advice should apply to the given target class
*/
boolean matches(Class<?> clazz);
/**
* Canonical ClassFilter instance that matches all classes.
*/
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
public interface MethodMatcher {
/**
* Perform static checking whether the given method matches.
* @param method the candidate method
* @param targetClass the target class
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class<?> targetClass);
/**
* Is this MethodMatcher dynamic, that is, must a final call be made on the
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
* runtime even when the 2-arg matches method returns {@code true}?
* @return whether or not a runtime match via the 3-arg
* {@link #matches(java.lang.reflect.Method, Class, Object[])} method
* is required if static matching passed
*/
boolean isRuntime();
/**
* Check whether there a runtime (dynamic) match for this method,
* which must have matched statically.
* @param method the candidate method
* @param targetClass the target class
* @param args arguments to the method
* @return whether there's a runtime match
* @throws UnsupportedOperationException if matches does not implement
* the 3-arg method (i.e. if isRuntime() returns false)
*/
boolean matches(Method method, Class<?> targetClass, Object... args);
/**
* Canonical MethodMatcher instance that matches all methods.
*/
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}Advisors combine advice with applicability information to determine when and where advice should be applied.
public interface Advisor {
/**
* Return the advice part of this aspect. An advice may be an
* interceptor, a before advice, a throws advice, etc.
* @return the advice that should apply if the pointcut matches
* @see org.aopalliance.intercept.MethodInterceptor
* @see BeforeAdvice
* @see ThrowsAdvice
* @see AfterReturningAdvice
*/
Advice getAdvice();
/**
* Return whether this advice is associated with a particular instance
* (for example, creating a mixin) or shared with all instances of
* the advised class obtained through the same Spring bean factory.
* <p><b>Note that this method is not currently used by the framework.</b>
* Typical Advisor implementations always return {@code true}.
* Use singleton/prototype bean definitions or appropriate programmatic
* proxy creation to ensure that Advisors have the correct lifecycle model.
* @return whether this advice is associated with a particular target instance
*/
boolean isPerInstance();
/**
* Common placeholder for an empty {@code Advice} to be preserved when
* no advice is applicable (e.g. from a {@code PointcutAdvisor} with a
* non-matching {@code Pointcut}).
*/
Advice EMPTY_ADVICE = new Advice() {};
}
public interface PointcutAdvisor extends Advisor {
/**
* Get the Pointcut that drives this advisor.
*/
Pointcut getPointcut();
}
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
/**
* Return the filter determining which target classes this introduction
* should apply to.
* <p>This represents the class part of a pointcut. Note that method
* matching doesn't make sense to introductions.
* @return the class filter
*/
ClassFilter getClassFilter();
/**
* Can the advised interfaces be implemented by the introduction advice?
* Invoked before adding an IntroductionAdvisor.
* @throws IllegalArgumentException if the advised interfaces can't be
* implemented by the introduction advice
*/
void validateInterfaces() throws IllegalArgumentException;
}Base interfaces for different types of advice that can be applied at join points.
public interface BeforeAdvice extends Advice {
// Marker interface for before advice
}
public interface AfterAdvice extends Advice {
// Marker interface for after advice
}
public interface MethodBeforeAdvice extends BeforeAdvice {
/**
* Callback before a given method is invoked.
* @param method the method being invoked
* @param args the arguments to the method
* @param target the target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
/**
* Callback after a given method successfully returns.
* @param returnValue the value returned by the method, if any
* @param method the method being invoked
* @param args the arguments to the method
* @param target the target of the method invocation. May be {@code null}.
* @throws Throwable if this object wishes to abort the call.
* Any exception thrown will be returned to the caller if it's
* allowed by the method signature. Otherwise the exception
* will be wrapped as a runtime exception.
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}
public interface ThrowsAdvice extends AfterAdvice {
// Tag interface for throws advice
// Methods must follow pattern:
// void afterThrowing([Method, args, target], Exception ex)
}Interfaces for implementing introductions (mixins) that add new interfaces to existing objects.
public interface IntroductionInfo {
/**
* Return the additional interfaces introduced by this Advisor or Advice.
* @return the introduced interfaces
*/
Class<?>[] getInterfaces();
}
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
// Combined interface for introduction interceptors
}
public interface DynamicIntroductionAdvice extends Advice {
/**
* Does this introduction advice implement the given interface?
* @param intf the interface to check
* @return whether the advice implements the specified interface
*/
boolean implementsInterface(Class<?> intf);
}
public interface IntroductionAwareMethodMatcher extends MethodMatcher {
/**
* Perform static checking whether the given method matches.
* This method is called when an AOP proxy is created, and need not
* account for the particular instance. If this method returns false
* or if the isRuntime() method returns false, no runtime check
* will be made.
* @param method the candidate method
* @param targetClass the target class
* @param hasIntroductions true if the object on whose behalf we are
* asking is the subject on one or more introductions; false otherwise
* @return whether or not this method matches statically
*/
boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions);
}Core interfaces for working with AOP proxies and target objects.
public interface TargetClassAware {
/**
* Return the target class behind the implementing object
* (typically a proxy configuration or an actual proxy).
* @return the target Class, or {@code null} if not known
*/
Class<?> getTargetClass();
}
public interface SpringProxy {
// Marker interface implemented by all proxies created by Spring's AOP infrastructure
}
public interface RawTargetAccess {
// Marker interface that indicates that the annotated method is eligible for
// invocation even if the target object is in "raw" mode
}
public interface ProxyMethodInvocation extends MethodInvocation {
/**
* Return the proxy that this method invocation was made through.
* @return the original proxy object
*/
Object getProxy();
/**
* Create a clone of this object. If cloning is done before {@code proceed()}
* is invoked on this object, {@code proceed()} can be invoked once per clone
* to invoke the joinpoint (and the rest of the advice chain) more than once.
* @return an invocable clone of this invocation.
* {@link #proceed()} can be called once per clone.
*/
MethodInvocation invocableClone();
/**
* Create a clone of this object. If cloning is done before {@code proceed()}
* is invoked on this object, {@code proceed()} can be invoked once per clone
* to invoke the joinpoint (and the rest of the advice chain) more than once.
* @param arguments the arguments that the cloned invocation is supposed to use,
* overriding the original arguments
* @return an invocable clone of this invocation.
* {@link #proceed()} can be called once per clone.
*/
MethodInvocation invocableClone(Object... arguments);
/**
* Set the arguments to be used on subsequent invocations in the any advice
* in this chain.
* @param arguments the argument array
*/
void setArguments(Object... arguments);
/**
* Add the specified user attribute with the given value to this invocation.
* <p>Such attributes are not used within the AOP framework itself.
* They are just kept as part of the invocation object, for use in
* special interceptors.
* @param key the name of the attribute
* @param value the value of the attribute, or {@code null} to reset it
*/
void setUserAttribute(String key, Object value);
/**
* Return the value of the specified user attribute.
* @param key the name of the attribute
* @return the value of the attribute, or {@code null} if not set
* @see #setUserAttribute
*/
Object getUserAttribute(String key);
}// Name-based pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("save*", "update*", "delete*");
// Custom method matcher
MethodMatcher customMatcher = new MethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.isAnnotationPresent(Transactional.class);
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
throw new UnsupportedOperationException("Not a runtime matcher");
}
};
// Composable pointcut
ComposablePointcut composite = new ComposablePointcut()
.union(pointcut)
.intersection(Pointcuts.forAnnotation(Service.class));public class LoggingBeforeAdvice implements MethodBeforeAdvice {
private static final Logger logger = LoggerFactory.getLogger(LoggingBeforeAdvice.class);
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
logger.info("Calling method: {} on target: {}",
method.getName(), target.getClass().getSimpleName());
}
}
public class CachingAfterReturningAdvice implements AfterReturningAdvice {
private final Map<String, Object> cache = new ConcurrentHashMap<>();
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
if (method.isAnnotationPresent(Cacheable.class)) {
String key = generateCacheKey(method, args);
cache.put(key, returnValue);
}
}
private String generateCacheKey(Method method, Object[] args) {
return method.getName() + Arrays.toString(args);
}
}Static utility methods for working with AOP proxies, target classes, and method detection.
public abstract class AopUtils {
/**
* Check whether the given object is a AOP proxy.
* @param object the object to check
* @return whether the object is a AOP proxy
*/
public static boolean isAopProxy(@Nullable Object object);
/**
* Check whether the given object is a JDK dynamic proxy.
* @param object the object to check
* @return whether the object is a JDK dynamic proxy
*/
public static boolean isJdkDynamicProxy(@Nullable Object object);
/**
* Check whether the given object is a CGLIB proxy.
* @param object the object to check
* @return whether the object is a CGLIB proxy
*/
public static boolean isCglibProxy(@Nullable Object object);
/**
* Determine the target class of the given bean instance which might be an AOP proxy.
* @param candidate the instance to check (might be an AOP proxy)
* @return the target class (or the plain class of the given object as fallback)
*/
public static Class<?> getTargetClass(Object candidate);
/**
* Determine whether the given method is an "equals" method.
* @param method the method to check
* @return whether the method is an "equals" method
*/
public static boolean isEqualsMethod(@Nullable Method method);
/**
* Determine whether the given method is a "hashCode" method.
* @param method the method to check
* @return whether the method is a "hashCode" method
*/
public static boolean isHashCodeMethod(@Nullable Method method);
/**
* Determine whether the given method is a "toString" method.
* @param method the method to check
* @return whether the method is a "toString" method
*/
public static boolean isToStringMethod(@Nullable Method method);
/**
* Can the given pointcut apply at all on the given class?
* @param pc the static or dynamic pointcut to check
* @param targetClass the class to test
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass);
/**
* Can the given advisor apply at all on the given class?
* @param advisor the advisor to check
* @param targetClass class we're testing
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass);
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-aop