Pointcut API in Spring

This section describes how Spring handles the crucial pointcut concept.

Concepts

Spring’s pointcut model enables pointcut reuse independent of advice types. You can target different advice with the same pointcut.

The org.springframework.aop.Pointcut interface is the central interface, used to target advices to particular classes and methods. The complete interface follows:

Java
public interface Pointcut {

	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();

}
Kotlin
interface Pointcut {

	fun getClassFilter(): ClassFilter

	fun getMethodMatcher(): MethodMatcher

}

Splitting the Pointcut interface into two parts allows reuse of class and method matching parts and fine-grained composition operations (such as performing a “union” with another method matcher).

The ClassFilter interface is used to restrict the pointcut to a given set of target classes. If the matches() method always returns true, all target classes are matched. The following listing shows the ClassFilter interface definition:

Java
public interface ClassFilter {

	boolean matches(Class clazz);
}
Kotlin
interface ClassFilter {

	fun matches(clazz: Class<*>): Boolean
}

The MethodMatcher interface is normally more important. The complete interface follows:

Java
public interface MethodMatcher {

	boolean matches(Method m, Class targetClass);

	boolean isRuntime();

	boolean matches(Method m, Class targetClass, Object[] args);
}
Kotlin
interface MethodMatcher {

	val isRuntime: Boolean

	fun matches(m: Method, targetClass: Class<*>): Boolean

	fun matches(m: Method, targetClass: Class<*>, args: Array<Any>): Boolean
}

The matches(Method, Class) method is used to test whether this pointcut ever matches a given method on a target class. This evaluation can be performed when an AOP proxy is created to avoid the need for a test on every method invocation. If the two-argument matches method returns true for a given method, and the isRuntime() method for the MethodMatcher returns true, the three-argument matches method is invoked on every method invocation. This lets a pointcut look at the arguments passed to the method invocation immediately before the target advice is to execute.

Most MethodMatcher implementations are static, meaning that their isRuntime() method returns false. In this case, the three-argument matches method is never invoked.

If possible, try to make pointcuts static, allowing the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.

Operations on Pointcuts

Spring supports operations (notably, union and intersection) on pointcuts.

Union means the methods that either pointcut matches. Intersection means the methods that both pointcuts match. Union is usually more useful. You can compose pointcuts by using the static methods in the org.springframework.aop.support.Pointcuts class or by using the ComposablePointcut class in the same package. However, using AspectJ pointcut expressions is usually a simpler approach.

AspectJ Expression Pointcuts

Since 2.0, the most important type of pointcut used by Spring is org.springframework.aop.aspectj.AspectJExpressionPointcut. This is a pointcut that uses an AspectJ-supplied library to parse an AspectJ pointcut expression string.

See the previous chapter for a discussion of supported AspectJ pointcut primitives.

Convenience Pointcut Implementations

Spring provides several convenient pointcut implementations. You can use some of them directly. Others are intended to be subclassed in application-specific pointcuts.

Static Pointcuts

Static pointcuts are based on the method and the target class and cannot take into account the method’s arguments. Static pointcuts suffice — and are best — for most usages. Spring can evaluate a static pointcut only once, when a method is first invoked. After that, there is no need to evaluate the pointcut again with each method invocation.

The rest of this section describes some of the static pointcut implementations that are included with Spring.

Regular Expression Pointcuts

One obvious way to specify static pointcuts is regular expressions. Several AOP frameworks besides Spring make this possible. org.springframework.aop.support.JdkRegexpMethodPointcut is a generic regular expression pointcut that uses the regular expression support in the JDK.

With the JdkRegexpMethodPointcut class, you can provide a list of pattern strings. If any of these is a match, the pointcut evaluates to true. (So, the result is effectively the union of these pointcuts.)

The following example shows how to use JdkRegexpMethodPointcut:

<bean id="settersAndAbsquatulatePointcut"
		class="org.springframework.aop.support.JdkRegexpMethodPointcut">
	<property name="patterns">
		<list>
			<value>.*set.*</value>
			<value>.*absquatulate</value>
		</list>
	</property>
</bean>

Spring provides a convenience class named RegexpMethodPointcutAdvisor, which lets us also reference an Advice (remember that an Advice can be an interceptor, before advice, throws advice, and others). Behind the scenes, Spring uses a JdkRegexpMethodPointcut. Using RegexpMethodPointcutAdvisor simplifies wiring, as the one bean encapsulates both pointcut and advice, as the following example shows:

<bean id="settersAndAbsquatulateAdvisor"
		class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
	<property name="advice">
		<ref bean="beanNameOfAopAllianceInterceptor"/>
	</property>
	<property name="patterns">
		<list>
			<value>.*set.*</value>
			<value>.*absquatulate</value>
		</list>
	</property>
</bean>

You can use RegexpMethodPointcutAdvisor with any Advice type.

Attribute-driven Pointcuts

An important type of static pointcut is a metadata-driven pointcut. This uses the values of metadata attributes (typically, source-level metadata).

Dynamic pointcuts

Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments as well as static information. This means that they must be evaluated with every method invocation and that the result cannot be cached, as arguments will vary.

The main example is the control flow pointcut.

Control Flow Pointcuts

Spring control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, although less powerful. (There is currently no way to specify that a pointcut executes below a join point matched by another pointcut.) A control flow pointcut matches the current call stack. For example, it might fire if the join point was invoked by a method in the com.mycompany.web package or by the SomeCaller class. Control flow pointcuts are specified by using the org.springframework.aop.support.ControlFlowPointcut class.

Control flow pointcuts are significantly more expensive to evaluate at runtime than even other dynamic pointcuts. In Java 1.4, the cost is about five times that of other dynamic pointcuts.

Pointcut Superclasses

Spring provides useful pointcut superclasses to help you to implement your own pointcuts.

Because static pointcuts are most useful, you should probably subclass StaticMethodMatcherPointcut. This requires implementing only one abstract method (although you can override other methods to customize behavior). The following example shows how to subclass StaticMethodMatcherPointcut:

Java
class TestStaticPointcut extends StaticMethodMatcherPointcut {

	public boolean matches(Method m, Class targetClass) {
		// return true if custom criteria match
	}
}
Kotlin
class TestStaticPointcut : StaticMethodMatcherPointcut() {

	override fun matches(method: Method, targetClass: Class<*>): Boolean {
		// return true if custom criteria match
	}
}

There are also superclasses for dynamic pointcuts. You can use custom pointcuts with any advice type.

Custom Pointcuts

Because pointcuts in Spring AOP are Java classes rather than language features (as in AspectJ), you can declare custom pointcuts, whether static or dynamic. Custom pointcuts in Spring can be arbitrarily complex. However, we recommend using the AspectJ pointcut expression language, if you can.

Later versions of Spring may offer support for “semantic pointcuts” as offered by JAC — for example, “all methods that change instance variables in the target object.”