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