@AspectJ Class level Annotation Advice with Annotation as method argument
aop, aspectj, java
Solution
The solution is actually quite simple. I am writing my code in native AspectJ style, I prefer it for clarity. You will be easily able to adjust it to @AspectJ annotation style:
public aspect LogTimeAspect {
pointcut publicMethod() : execution(public * *(..));
before(LogExecutionTime logAnn) : publicMethod() && @annotation(logAnn) {
System.out.println(thisJoinPointStaticPart + " -> " + logAnn.level());
}
before(LogExecutionTime logAnn) : publicMethod() && @within(logAnn) {
System.out.println(thisJoinPointStaticPart + " -> " + logAnn.level());
}
}
The output is as follows:
execution(void Operator.operate()) -> Method_Level_Invocation
execution(void Operator.operate()) -> Class_Level_Invocation
execution(void Operator.operate1()) -> Class_Level_Invocation
As you can see,
- there is no need for `around()` advice, `before()` is sufficient unless you want to manipulate any parameters or block the captured method executions,
- you can bind the annotations in question via `@annotation()` or `@within()` to named parameters if you just use the correct syntax.
Enjoy! `:-)`
Update: Here is the @AspectJ version of the aspect for your convenience and because you seemed to have problems adapting my solution from the native syntax:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LogTimeAspect {
@Pointcut("execution(public * *(..))")
public void publicMethod() {}
@Around("publicMethod() && @annotation(logAnn)")
public Object LogExecutionTimeByMethod(ProceedingJoinPoint joinPoint, LogExecutionTime logAnn) throws Throwable {
System.out.println(joinPoint + " -> " + logAnn.level());
return joinPoint.proceed();
}
@Around("publicMethod() && @within(logAnn)")
public Object LogExecutionTimeByClass(ProceedingJoinPoint joinPoint, LogExecutionTime logAnn) throws Throwable {
System.out.println(joinPoint + " -> " + logAnn.level());
return joinPoint.proceed();
}
}
The results will be identical to my original version.
Problem
How can I get the annotation to be passed as the argument for the Advice defined for class-level annotation? Is it possible? From the post here I am able to get the point cut that identifies all the public method in the class which is marked by a specific Annotation. I am able to get the advice applied as well. However, I don’t know how to get the annotation variable passed as argument in above case. For a method-level annotation, I am able to get the pointcut and advice in which I can get the annotation passed as argument, but I don’t know how to achieve the same for class-level annotation. The below code works, but I need to get the annotation as the argument for the advice “LogExecutionTimeByClass” in below program and I couldn’t able to get appropriate advice or pointcut for the same. Annotation: ``` import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface LogExecutionTime { String level(); } ``` Aspect: ``` import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class LogTimeAspect { /* * Pointcut to match all the public methods. */ @Pointcut("execution(public * *(..))") public void publicMethod() {} /* * Advice for the public methods that are marked with Annotation "LogExecutionTime" and it works as expected no issue. */ @Around("publicMethod() && @annotation(annotation) ") public Object LogExecutionTimeByMethod(final ProceedingJoinPoint joinPoint,final LogExecutionTime annotation) throws Throwable { System.out.println("Invoking the method " +joinPoint.getSignature() +" by LogExecutionTimeByMethod Advice"); return joinPoint.proceed(); } /* * Pointcut to match all the public methods that are defined under the Class marked with Annotation LogExecutionTime. */ @Pointcut("within(@LogExecutionTime *)") public void beanAnnotatedWithMonitor() {} @Pointcut("publicMethod() && beanAnnotatedWithMonitor()") public void publicMethodInsideAClassMarkedWithAtMonitor() {} /* * Below Advice works but I need the LogExecutionTime annotation as an argument to below method. (similar to the advice "LogExecutionTimeByMethod" * defined above) */ @Around("publicMethodInsideAClassMarkedWithAtMonitor()") public Object LogExecutionTimeByClass(final ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Invoking the method " +joinPoint.getSignature() +" by LogExecutionTimeByClass Advice"); //System.out.println("Invoked by " + annotation.value()); //Need the Annotation Variable here as well... return joinPoint.proceed(); } /* */ } ``` Annotated Class: ``` @LogExecutionTime(level="Class_Level_Invocation") public class Operator { @LogExecutionTime(level="Method_Level_Invocation") public void operate() throws InterruptedException { Thread.sleep(1000); } public void operate1() throws InterruptedException { Thread.sleep(1000); } } ``` Main Program: ``` public class AspectJMain { public static void main(String[] args) throws InterruptedException { Operator op = new Operator(); op.operate(); op.operate1(); } } ``` Output: ``` Invoking the method void Operator.operate() by LogExecutionTimeByMethod Advice Invoking the method void Operator.operate() by LogExecutionTimeByClass Advice Invoking the method void Operator.operate1() by LogExecutionTimeByClass Advice ``` Please note that using Spring is is not an option. I have to use AspectJ compiler. I compiled my classes and packaged them as jar and use ApsectJ compiler to woven the aspect using below command. ajc -inpath core.jar -outjar ..\lib\core_woven.jar -1.5 Any pointer would be helpful.