Spring Aop @Annotation Pointcut Designator Example

0

At times you may want to match join points based on annotation. This is comes handy when you may not be able to find any common pattern among the methods you may want to match in which case you simply annotate those methods with a custom annotation and write a pointcut expression that uses the annotation.

Custom annotation

To demonstrate the example lets first define a custom annotation. We would like to enable diagnostics for those methods which are likely to take more time to execute. We may also want to configure the annotation further to record an event about the method, start, end time and the arguments. You may even log the JVM diagnostics.

EnableDiagnostics:

package com.javarticles;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target( { ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableDiagnostics {
    boolean recordEvent() default false;
}

We will annotate the long running methods with @EnableDiagnostics. Note that this annotation can be applied to both method level and type level.

TestBean:

package com.javarticles;

public class TestBean {

    @EnableDiagnostics
    public void longRunningProcess1() {
        System.out.println("Start of longRunningProcess1");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        System.out.println("End of longRunningProcess1");
    }

    @EnableDiagnostics(recordEvent=true)
    public void longRunningProcess2() {
        System.out.println("Start of longRunningProcess2");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
        }
        System.out.println("End of longRunningProcess2");
    }
}

Now, we will write a pointcut expression to match all methods that use @EnableDiagnostics annotation.
We will use the @annotation(Annotation) to match the join points to the annotation passed in.

  1. Match methods that use @EnableDiagnostics.
    @Before("@annotation(EnableDiagnostics)")

    . This will be called before the actual execution. You may want to log JVM diagnostic details here like heap memory, non-heap memory etc.

  2. You may want to bind the @annotation used to a method parameter so that the annotation object is available in the advice body. can also be used in a binding form.
    @Around("execution(* *(..)) && @annotation(enableDiagnostics)")
    public void calculateExecutionTime(ProceedingJoinPoint pjp,
                EnableDiagnostics enableDiagnostics) throws Throwable {
    ....
    }
    

Pointcut Expression using @Annotation

Here the annotation object is available as enableDiagnostics argument. This is helpful if want to take some action based on the configured annotation attributes. Like in our example, we will not only log the execution time but also record event if enableDiagnostics.recordEvent() is true.

AspectExpressionsBean:

package com.javarticles;

import java.util.Arrays;

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

@Aspect
public class AspectExpressionsBean {

    @Before("@annotation(EnableDiagnostics)")
    public void showJvmDiagnostics() {         
        System.out.println("Show Jvm Diagnostics");
    }

    @Around("execution(* *(..)) && @annotation(enableDiagnostics)")
    public void calculateExecutionTime(ProceedingJoinPoint pjp,
            EnableDiagnostics enableDiagnostics) throws Throwable {
        long start = System.currentTimeMillis();
        pjp.proceed();
        long end = System.currentTimeMillis();
        System.out.println("Execution time(" + (end - start) + ")");
        if (enableDiagnostics.recordEvent()) {
            String methodName = pjp.getSignature().getName();
            String args = Arrays.asList(pjp.getArgs()).toString();
            System.out.println("Record event");
            System.out.println("Method: " + methodName + ", args: " + args
                    + ", start: " + start + ", end: " + end
                    + ", execution time: " + (end - start));
        }
    }
}

We need to declare the test bean and the class containing the advices. You must include <aop:aspectj-autoproxy/> for the spring to automatically parse the aspectj annotations.

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<bean id="testBean" class="com.javarticles.TestBean"/>	
        <aop:aspectj-autoproxy/>
	<bean id="aspectExpressionsBean" class="com.javarticles.AspectExpressionsBean"/>
	
</beans>

Run the [email protected] Pointcut Designator Example

To test the aspect we just need to load the context, get the test bean and call the methods annotated with EnableDiagnostics.

SpringAopAnnotationPointcutDesignatorExample:

package com.javarticles;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAopAnnotationPointcutDesignatorExample {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        try {
            TestBean testBean = (TestBean) context.getBean("testBean");
            testBean.longRunningProcess1();
            testBean.longRunningProcess2();
        } finally {
            context.close();
        }
    }
}

Output:

Show Jvm Diagnostics
Start of longRunningProcess1
End of longRunningProcess1
Execution time(2011)
Show Jvm Diagnostics
Start of longRunningProcess2
End of longRunningProcess2
Execution time(2000)
Record event
Method: longRunningProcess2, args: [], start: 1441350524887, end: 1441350526887, execution time: 2000

Download the source code

This was an example about Spring Aop @Annotation Pointcut Designator.

You can download the source code here: springAopAnnotationPointcutDesignator.zip
Share.

Comments are closed.