Introduction to Pointcut Expressions in Spring

1. Overview

In this tutorial we will discuss the Spring AOP pointcut expression language.

We will first introduce some terminology used in aspect-oriented programming. A join point is a step of the program execution, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution. A pointcut is a predicate that matches the join points and a pointcut expression language is a way of describing pointcuts programmatically.

2. Usage

A pointcut expression can appear as a value of the @Pointcut annotation:

@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {}

The method declaration is called the pointcut signature. It provides a name that can be used by advice annotations to refer to that pointcut.

@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
    ...
}

A pointcut expression could also appear as the value of the expression property of an aop:pointcut tag:

<aop:config>
    <aop:pointcut id="anyDaoMethod"
      expression="@target(org.springframework.stereotype.Repository)"/>
</aop:config>

3. Pointcut Designators

A pointcut expression starts with a pointcut designator (PCD), which is a keyword telling Spring AOP what to match. There are several pointcut designators, such as the execution of a method, a type, method arguments, or annotations.

3.1 execution

The primary Spring PCD is execution, which matches method execution join points.

@Pointcut("execution(public String org.baeldung.dao.FooDao.findById(Long))")

This example pointcut will match exactly the execution of findById method of the FooDao class. This works, but it is not very flexible. Suppose we would like to match all the methods of the FooDao class, which may have different signatures, return types, and arguments. To achieve this we may use wildcards:

@Pointcut("execution(* org.baeldung.dao.FooDao.*(..))")

Here the first wildcard matches any return value, the second matches any method name, and the (..) pattern matches any number of parameters (zero or more).

3.2 within

Another way to achieve the same result from the previous section is by using the within PCD, which limits matching to join points of certain types.

@Pointcut("within(org.baeldung.dao.FooDao)")

We could also match any type within the org.baeldung package or a sub-package.

@Pointcut("within(org.baeldung..*)")

3.3 this and target

this limits matching to join points where the bean reference is an instance of the given type, while target limits matching to join points where the target object is an instance of the given type. The former works when Spring AOP creates a CGLIB-based proxy, and the latter is used when a JDK-based proxy is created. Suppose that the target class implements an interface:

public class FooDao implements BarDao {
    ...
}

In this case, Spring AOP will use the JDK-based proxy and you should use the target PCD because the proxied object will be an instance of Proxy class and implement the BarDao interface:

@Pointcut("target(org.baeldung.dao.BarDao)")

On the other hand if FooDao doesn’t implement any interface or proxyTargetClass property is set to true then the proxied object will be a subclass of FooDao and the this PCD could be used:

@Pointcut("this(org.baeldung.dao.FooDao)")

3.4 args

This PCD is used for matching particular method arguments:

@Pointcut("execution(* *..find*(Long))")

This pointcut matches any method that starts with find and has only one parameter of type Long. If we want to match a method with any number of parameters but having the fist parameter of type Long, we could use the following expression:

@Pointcut("execution(* *..find*(Long,..))")

3.5 @target

The @target PCD (not to be confused with the target PCD described above) limits matching to join points where the class of the executing object has an annotation of the given type:

@Pointcut("@target(org.springframework.stereotype.Repository)")

3.6 @args

This PCD limits matching to join points where the runtime type of the actual arguments passed have annotations of the given type(s). Suppose that we want to trace all the methods accepting beans annotated with @Entity annotation:

@Pointcut("@args(org.baeldung.aop.annotations.Entity)")
public void methodsAcceptingEntities() {}

To access the argument we should provide a JoinPoint argument to the advice:

@Before("methodsAcceptingEntities()")
public void logMethodAcceptionEntityAnnotatedBean(JoinPoint jp) {
    logger.info("Accepting beans with @Entity annotation: " + jp.getArgs()[0]);
}

3.7 @within

This PCD limits matching to join points within types that have the given annotation:

@Pointcut("@within(org.springframework.stereotype.Repository)")

Which is equivalent to:

@Pointcut("within(@org.springframework.stereotype.Repository *)")

3.8 @annotation

This PCD limits matching to join points where the subject of the join point has the given annotation. For example we may create a @Loggable annotation:

@Pointcut("@annotation(org.baeldung.aop.annotations.Loggable)")
public void loggableMethods() {}

Then we may log execution of the methods marked by that annotation:

@Before("loggableMethods()")
public void logMethod(JoinPoint jp) {
    String methodName = jp.getSignature().getName();
    logger.info("Executing method: " + methodName);
}

4. Combining Pointcut Expressions

Pointcut expressions can be combined using &&, || and ! operators:

@Pointcut("@target(org.springframework.stereotype.Repository)")
public void repositoryMethods() {}

@Pointcut("execution(* *..create*(Long,..))")
public void firstLongParamMethods() {}

@Pointcut("repositoryMethods() && firstLongParamMethods()")
public void entityCreationMethods() {}

5. Conclusion

In this quick intro to Spring AOP and pointcuts, we illustrated some examples of pointcut expressions usage.

The full set of examples could be found in my github project – this is an Eclipse based project, so it should be easy to import and run as it is.

Leave a Reply

Your email address will not be published.