Spring AOP : Pointcuts and JoinPoints

A JoinPoints is a point in program execution where you can add additional behavior, 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 join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

Pointcuts use class, field, constructor, and method expressions to specify the actual joinpoint that should be intercepted/watched.

We have been using pointcut expressions in our @Before Advice.

@Before(“execution(public String getName())”)
public void logMessage(){ //some method

Above Advice (Before) will run the logMessage() method whenever a public String getName() method is going to be executed. A pointcut declaration has two parts: a signature comprising a name and any parameters, and a pointcut expression that determines exactly which method executions we are interested in.

Spring AOP supports the following AspectJ pointcut designators (PCD) for use in pointcut expressions – execution – for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP.  There are many others (within, args, target etc.), which we will later.

The most typical pointcut expressions are used to match a number of methods by their signatures. A common method based pointcut expression is something like

PCD(<method scope> <return type> <fully qualified class name>.*(arguments))

  1. method scope: Advice will be applied to all the methods having this scope. For e.g., public, private, etc. Please note that Spring AOP only supports advising public methods.
  2. return type: Advice will be applied to all the methods having this return type.
  3. fully qualified class name: Advice will be applied to all the methods of this type. If the class and advice are in the same package then package name is not required
  4. parameters: You can also filter the method names based on the types. Two dots(..) means any number and type of parameters.

There are two types of wildcards you can use within pointcut expressions

  • * Is a regular wildcard that matches zero or more characters. It can be used within any type expression, field, or method name, but not in an annotation expression
  • .. Is used to specify any number of parameters in an constructor or method expression.
    .. following a package-name is used to specify all classes from within a given package ut not within sub-packages. e.g org.acme.. matches org.acme.Foo and org.acme.Bar, but it does not match org.acme.sub.SubFoo.

Some examples

  • execution(* com.aspects.pointcut.DemoClass.*(..)) : This advice will be applied to all the methods of DemoClass.
  • execution(* DemoClass.*(..)): You can omit the package if the DemoClass and the advice is in the same package.
  • execution(public * DemoClass.*(..)): This advice will be applied to the public methods of DemoClass.
  • execution(public int DemoClass.*(..)): This advice will be applied to the public methods of DemoClass and returning an int.
  • execution(public int DemoClass.*(int, ..)): This advice will be applied to the public methods of DemoClass and returning an int and having first parameter as int.
  • execution(public int DemoClass.*(int, int)): This advice will be applied to the public methods of DemoClass and returning an int and having both parameters as int.

Named pointcut expression

Sometimes it happens that we may need same pointcut at multiple places. But since pointcut expressions are lengthy, it will be good if instead of repeating we can give a logical name to that pointcut and refer to it by logical name. Usually these pointcuts are defined in a seperate Pointcut config file: PointcutConfig.java


package mynotes.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class PointcutConfig {
 
 @Pointcut("execution(* mynotes.aop.model.SoftwareDevelopers.getName())") //This is a pointcut expression
 public void dummyMethod() {} //This is a name given to the pointcut expression
 
 //Other Such Pointcuts

}


LoggingUsingPointcutConfig.java


package mynotes.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoggingUsingPointcutConfig {
 
 @Before("PointcutConfig.dummyMethod()")
 public void logMessage1(){
 System.out.println("Before Logging Aspect from PointcutConfig.dummyMethod() in logMessage1()");
 }
 
 @Before("PointcutConfig.dummyMethod()")
 public void logMessage2(){
 System.out.println("Before Logging Aspect from PointcutConfig.dummyMethod() in logMessage2()");
 }


}


Notice that instead of writing the pointcut expression twice we used the dummyMethod() name. Note that the code inside, if any, in dummyMethod() will not get executed. It is only used to name the pointcut.

spring_pointcutJoinpoint.xml


<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:task="http://www.springframework.org/schema/task"
 xsi:schemaLocation="http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
 http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<aop:aspectj-autoproxy />

<bean id="pointcutConfig" class="mynotes.aop.aspect.PointcutConfig" />
 
 <bean id="loggingAspect" class="mynotes.aop.aspect.LoggingUsingPointcutConfig" />
 
 <bean id="softwareDevelopers" class="mynotes.aop.model.SoftwareDevelopers">
 <property name="name" value="developer1"></property>
 </bean>
 <bean id="managers" class="mynotes.aop.model.Managers">
 <property name="name" value="manager1"></property>
 </bean>

<bean id="employeeService" class="mynotes.aop.service.EmployeeService"
 autowire="byName" />

</beans>

Other classes will remain same as previous tutorial. AopMainPointCutJoinPoint.java to test :


package mynotes.aop.main;

import mynotes.aop.service.EmployeeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopMainPointCutJoinPoint {

public static void main(String[] args) {
 
 ApplicationContext context = new ClassPathXmlApplicationContext("spring_pointcutJoinpoint.xml");
 EmployeeService employeeService=context.getBean("employeeService",EmployeeService.class);
 
 System.out.println(employeeService.getSoftwareDevelopers().getName());
 
 System.out.println(employeeService.getManagers().getName());
 }

}


Output:


Before Logging Aspect from PointcutConfig.dummyMethod() in logMessage1()
Before Logging Aspect from PointcutConfig.dummyMethod() in logMessage2()
developer1
manager1

Suppose we have a requirement where to log for all methods inside SoftwareDevelopers. One way to do is to write the execution expreassion. There is another way using PCD – within.

within – limits matching to join points within certain types.

within(type name)

  • within(com.aspects.blog.package.*) : This will match all the methods in all classes of com.aspects.blog.package.
  • within(com.aspects.blog.package..*) : This will match all the methods in all classes of com.aspects.blog.package and its sub packages. The only difference is the extra dot(.) after package.
  • within(com.aspects.blog.package.DemoClass) : This will match all the methods in the DemoClass.
  • within(DemoClass) : Again, if the target class is located in the same package as this aspect, the package name can be omitted.
  • within(DemoInterface+) : This will match all the methods which are in classes which implement DemoInterface.

Combining Pointcut Expressions

Pointcut expressions can be combined using && (and), ||(or), and !(not). For example,

within(DemoInterface1+) || within(DemoInterface2+) //will match all join point in all classes which implement DemoInterface1 or DemoInterface2

Similarly,

execution(<something>) ! excution (<something>)

 

Till now we have seen the logMessage() method being executed Before. Suppose we wan tto know which method spring is calling that satisfies the pointcut expression and hence the logMessage() is runing before it. This can be achieved by passing JointPoint in the argument. Adding following inn out Aspect :


Notice that we got the whole object using getTarget(). Adding this to test:



EmployeeService employeeService=context.getBean("employeeService",EmployeeService.class);

System.out.println(employeeService.getManagers().getName());

Output:


Method : getNameat Mon Jul 14 00:19:39 IST 2014
New Name

Notice the manager name got from 'manager1' to 'New Name'.

Suppose we want a aspect to run for method that takes in String argumets. We can use PDC - args. Adding following aspect


@Before("args(String)")
public void logMessage(){
System.out.println("Before Logging Aspect from args");
}

Calling from test class :


System.out.println(employeeService.getManagers().getName());
employeeService.getManagers().setName("Manager2");
System.out.println(employeeService.getManagers().getName());

Output:


manager1
Before Logging Aspect from args
Manager2

If you want to know what parameter is being passed to the method, and use that parameter in your logMessage() method, then


@Before("args(myname)")
public void logMessage(String myname){
System.out.println("Before Logging Aspect from args where name="+myname);
}

Notice that the logMessage() has an arument String 'myname'. This is the same name as that of args. Output:


manager1
Before Logging Aspect from args where name=Manager2
Manager2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: