Spring AOP – Pointcut Expressions

Spring AOP – Pointcut Expressions

Pointcut refers to a collection of join points that specify where advice to be applied. In other words, pointcut represents a set of various join points. It defines that “where advice should be executed.” 

Let’s understand the use of pointcut through an example.

Suppose, there are several join points in the system on which we want to apply aspect. Instead of applying aspects to every join point, we can use pointcut expression.

To declare a pointcut, we use @Pointcut annotation. It is defined in the org.aspectj.lang.annotation.Pointcut package. A pointcut declaration has two parts:

  • Pointcut Expression: The value of @Pointcut annotation is referred to as Pointcut Expression.
  • Pointcut Signature: The method which is annotated by @Pointcut is known as Pointcut Signature.

Syntax of pointcut expression

Syntax of pointcut expression

The above image shows the syntax of pointcut expression.The return-type-pattern, method-name-pattern, and param-pattern are mandatory fields, and the remaining fields are optional.

Following code snippet shows the Pointcut Expression:

@Pointcut("execution(public void newAccount())") //the pointcut expression
  public void beforeNewAccount() { }          //the pointcut signature 

In the above code, pointcut expression is “@Pointcut(execution(public void newAccount())”, and pointcut signature is “beforeNewAccount()”.

Pointcut Expressions

Let’s understand the pointcut expressions with some examples. Before learning the Pointcut Expressions, we should have some knowledge about Wildcards.

Wildcards  

Wildcards are used by AspectJ to construct the pointcuts in order to capture the join points that shares common characteristics. There are two types of wildcards used in pointcuts:

  • The (*) wildcard is used to denote any number of characters except the period. Also, it is used in a type signature pattern to specify the part of a class, interface, or package name.
  • The (. .) wildcard is used to denote any number of characters, including any number of periods. Also, it denotes all direct and indirect sub-packages.  

Examples of pointcut expressions using wildcards

@Pointcut("execution(* newAccount())")

In the above code snippet, * wildcard is used for return-type of the method. It means the pointcut is applied on every method named newAccount() irrespective of return-type. 

@Pointcut("execution( * new*() )")

In the above code snippet, we have used two wildcards in the expression. The first wildcard is for return-type, and the second wildcard for the method-name-pattern. It means pointcut is applied to every no-argument method, which starts with a new word.

@Pointcut("execution(* newAccount(..))") 

In the above code snippet, we have used two types wildcards (* and ..) in the pointcut expression. The first wildcard is used for return-type, and the second wildcard (..) is used for param-pattern. It means the pointcut expression is applied to every argument of the method newAccount().

@Pointcut("execution(void com.app.SpringAspectOP.StudentAccountDao.*())")

In the above code snippet, * wildcard is used for method-name-pattern. It means that the expression is applied to all the void methods available in the StudentAccountDao class.

Pointcut expressions are used with all types of advices. Here, we are going to use the @Before advice with pointcut expressions.

Spring Pointcut expression using @Before annotation

Let’s create some different examples of pointcut expression with @Before advice.

  1. Match with void method named: newAccount

In this example, we are going to create a pointcut expression that will match with all the void methods named newAccount() in the application.

Following are the steps to create an example of pointcut expression:

  • Create all DAO classes

In this step, we are going to create all the DAO classes required in the application. We are taking two DAO classes named StudentAccountDao and UserAccountDao. Both the classes contain newAccount() methods.

StudentAccountDao.java

import org.springframework.stereotype.Component;
@Component
public class StudentAccountDao {
public void newAccount() {
System.out.println(getClass() + " : To open a new acount for Student ");
}
} 

UserAccountDao.java

import org.springframework.stereotype.Component;
@Component
public class UserAccountDao {
public void newAccount() {
System.out.println(getClass() + " : To open a new account for User");
}
} 
  • Create a class that performs component scanning

In this step, we are going to create a class named DemoAOP that performs the component-scanning.

DemoAOP.java

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(" com.app.SpringAspectOP ")
public class DemoAOP {
} 
  • Create a class for Aspect logic

In this step, we are going to create a class named AspectClass that contains the aspect logic (pointcut expressions).

AspectClass.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AspectClass {
@Before("execution(public void newAccount())")
public void BeforeNewAccount() { 
System.out.println("@Before Advice - Executing the @Before advice ");
}
} 
  • Create the main class

In this step, we are going to create the main class named App that contains the main method.

App.java

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App 
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext appcontext = new AnnotationConfigApplicationContext(DemoAOP.class);
// defining the Student dao class
StudentAccountDao studacc = appcontext.getBean("studentAccountDao", StudentAccountDao.class);
// defining the user dao class
UserAccountDao useracc = appcontext.getBean("userAccountDao", UserAccountDao.class);
studacc.newAccount();
System.out.println("\n");
useracc.newAccount();
appcontext.close();
}
} 

Output

The following output shows that the pointcut expression is applied to both methods named newAccount().

pointcut expression
  • Only match with: StudentAccountDao.newAccount()

In the previous example, we have seen that the pointcut expression is applied on both the methods. Now, we are going to modify the pointcut expression of the previous example.

Instead of (void newAccount()),we are going to use (void com.app.SpringAspectOP.StudentAccountDao.newAccount()) in the pointcut expression. We are providing the actual path of the method. It means that the qualified name of the class that contains the method.

AspectClass.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AspectClass {
@Before("execution(public void com.app.SpringAspectOP.StudentAccountDao.newAccount())")
public void BeforeNewAccount() {
System.out.println(" @Before Advice - Executing the @Before advice ");
}
} 

In the above code snippet, we have specified the method on which the pointcut expression was applied.

Output

The following output shows that the pointcut expression is applied only to the newAccount() method of com.app.SpringAspectOP.StudentAccountDao class.

pointcut expression is applied
  • Match with any method starting with “new” of any class

In the following example, we are going to use wildcards (*) for return-type and method-name-pattern. It means the pointcut expression is applied to all the methods starting with the “new” keyword in the application.

This example is very similar to the previous one, so we are doing some modifications to the previous one.

Usernew.java

import org.springframework.stereotype.Component;
@Component
public class Usernew {
public String newUserAcc() {
System.out.println(getClass() + " : To add a new account for User");
return " In newUserAccount method " ;
}
} 

AspectClass.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AspectClass {
@Before("execution(public * new*())")
public void BeforeNewAccount() { 
System.out.println("@Before Advice - Executing the @Before advice ");
}
} 

App.class

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App 
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext appcontext = new AnnotationConfigApplicationContext(DemoAOP.class);
// defining the Student dao class
StudentAccountDao studacc = appcontext.getBean("studentAccountDao", StudentAccountDao.class);
// defining the user dao class 
Usernew useracc = appcontext.getBean("usernew", Usernew.class);
studacc.newAccount();
System.out.println("\n");
useracc.newUserAcc();
appcontext.close();
}
} 

Output

The following output shows that the pointcut expression is applied to both the methods starting with the “new” word.

pointcut expression is applied to both the methods
  • Match methods based on return type “void”

In this example, we are going to set the modifier-type to void. The pointcut expression is applied to all the void methods starting with the new keyword.

As we have discussed, the modifier is optional in the pointcut expression. So we are going to remove the modifier in the following example.

Here, we are taking the previous example for modification. The changes are made in the following classes:

AspectClass.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AspectClass {
@Before("execution( void new*() )")
public void BeforeNewAccount() {
System.out.println("@Before Advice - Executing the @Before advice ");
}
} 

Usernew.java

import org.springframework.stereotype.Component;
@Component
public class Usernew {
public String newUserAcc() {
System.out.println(getClass() + " : To add a new account for User");
return " In newUserAccount method " ;
}
} 

Output

The following output shows that the pointcut expression is applied only on the void method, i.e., only on the newAccount() method of StudentAccountDao class.

pointcut expression is applied only on the void method

Pointcut Declaration Example

In this example, we are going to define the pointcut expression using the @Pointcut annotation.

Here, we are modifying the previous example:

StudentAccountDao.java

import org.springframework.stereotype.Component;
@Component
public class StudentAccountDao {
public void newAccount() {
System.out.println(getClass() + " : To open a new acount for Student ");
}
public void newCard() {
System.out.println(getClass() + " : To buy a new Card" ); 
}
} 

Usernew.java

import org.springframework.stereotype.Component;
@Component
public class Usernew {
public String newUserAcc() {
System.out.println(getClass() + " : To add a new account for User");
return "In newUserAccount method" ;
}
public void newSystem() {
System.out.println(getClass() + " : Get new system ");
}
} 

AspectClass.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class AspectClass {
@Pointcut("execution(public * com.app.SpringAspectOP.*.*(..))")
public void myPointcutExpression() { }
@Before("myPointcutExpression()")
public void BeforeNewAccount() {
System.out.println("@Before Advice - Executing the @Before advice ");
}
} 

In the above code, we have declared the pointcut expression separately using the @Pointcut annotation. The method that is annotated with @Pointcut annotation is given as the pointcut expression to @Before annotation. The main advantage of separately declaring the pointcut expression is we can easily reuse the code when needed.

App.java

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App 
{
public static void main( String[] args )
{
AnnotationConfigApplicationContext appcontext = new AnnotationConfigApplicationContext(DemoAOP.class);
// defining the Student dao class
StudentAccountDao studacc = appcontext.getBean("studentAccountDao", StudentAccountDao.class);
// defining the user dao class 
Usernew useracc = appcontext.getBean("usernew", Usernew.class);
studacc.newAccount();
studacc.newCard();
System.out.println("\n");
useracc.newUserAcc();
useracc.newSystem();
appcontext.close();
}
}

Output

The following output shows that the @Before advice is applied to every method available in the package.

Before advice is applied to every method available in the package.

In the above examples, we have discussed the pointcut expressions with @Before annotation. Similarly, we can use them with other advice types such as @After and @Around advice.