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
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.
- 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().
- 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.
- 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.
- 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 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.
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.