#AspectJ Class level Annotation Advice with Annotation as method argument - java

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.

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.

Related

Spring AOP - Custom Annotation for Class and Method

Existing answers gives nice explanation on how to use Custom Annotation for method execution time logging. I am wondering if there is way to use same annotation for both Class and Method, but Pointcut should be different where it is used.
#LogExecutionTime
public class MyServiceImpl implements MyService {
public void run(){
// logic
}
public void walk(){
// logic
}
private void breather(){
// logic
}
}
If Annotation is used for class all methods inside class should be considered for Execution Time Logging in Aspect Class (like execution(* com.me.package.MyServiceImpl.*(..))). However if the Annotation is only used for single method inside the class, it is should also consider that only method in Aspect Logging Class. (like execution(* com.you.package.YourServiceImpl.forward(..))).
public class YourServiceImpl implements YourService {
#LogExecutionTime
public void forward(){
// logic
}
#LogExecutionTime
public void backward(){
// logic
}
private void upward(){
// logic
}
}
Annotation Class
package com.myproj.core.utils.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
#Retention(RUNTIME)
public #interface LogExecutionTime {
}
Aspect Class for Annotation (using pointcuts as suggested by #kriegaex)
package com.myproj.core.utils;
import java.time.Duration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import java.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Aspect class to Log Method Execution time
*/
#Aspect
public class MyMethodExecutionLoggingAspect {
private static final Logger LOG = LoggerFactory.getLogger(MyMethodExecutionLoggingAspect.class);
/**
* This method will log Method Execution Time
*
* #param joinPoint
* #return object
* #throws Throwable
*/
#Around("execution(* (#com.myproj.core.utils.annotation.LogExecutionTime *).*(..)) || execution(#com.myproj.core.utils.annotation.LogExecutionTime * *(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
Instant start = Instant.now();
try {
return joinPoint.proceed();
} finally {
long end = Duration.between(start, Instant.now()).toMillis();
if (end > 0) {
LOG.debug("METHOD EXECUTION TIME LAPSED: {}ms | {}.{}", end, className, methodName);
}
}
}
}
Spring Bean Definition in spring.xml
<bean class="com.myproj.core.utils.MyMethodExecutionLoggingAspect" />
Your sample code with #annotation() does not make sense because you do not specify a method annotation type. It should be something like #annotation(fully.qualified.AnnotationType) instead.
For matching class annotations you want to use #within(fully.qualified.AnnotationType), as described here.
So you could use a pointcut like
#annotation(fully.qualified.AnnotationType)
|| #within(fully.qualified.AnnotationType)
Alternatively, according to my answer here, you could also use the more cryptic version
execution(* (#fully.qualified.AnnotationType *).*(..))
|| execution(#fully.qualified.AnnotationType * *(..))

How do I perform something on each method call of an annotated method?

I want to write an annotation in Java, which executes something before and after execution of the annotated method, similar to what can be done in Spring with aspects.
I've already tried a Spring aspect, but it only works for Beans (as this guy here mentioned) and I want to stay independent from the Spring framework.
A simple class that writes a String to the console:
public class Main {
public static void main(String[] args) {
say("How are you?");
}
#Hello
private static void say(String s) {
System.out.println(s);
}
}
The associated annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Hello {}
And I need something like (deduced from the Spring aspect)
public Object process(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("Hello");
Object proceed = null;
try {
proceed = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("world!");
return proceed;
}
I want the following output:
Hello
How are you?
world!
Edit:
I created the following Aspect (without annotations), but it does not work
#lombok.extern.java.Log
public aspect Log {
pointcut methodExecuted():execution(* **(..));
void around(): methodExecuted() {
log.info("Hello");
proceed();
log.info("world!");
}
}
Where is my mistake?
Assuming that you successfully compiled your aspect with the AspectJ compiler, it should work with the code you used, only that it would log all method executions, i.e. also main(..), so you would se the aspect's output twice before and after "How are you?". If you don't see anything probably you made a mistake in setting up your build system.
You should change the pointcut to actually limit logging to annotated methods: execution(* *(..)) && #annotation(Hello). Furthermore if your around advice has a void return type, logging will not work with non-void methods. So you should rather use a return type of Object and actually return the result of proceed().
I also strongly urge you to not just blindly wield a powerful tool like AspectJ but also study some documentation before you do. It is quite obvious that you have not done so or only very cursorily. Then you get the effect of being a not-so-capable-user with a tool. ;-)
Here is my MCVE:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface Hello {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
say("How are you?");
}
#Hello
private static void say(String s) {
System.out.println(s);
}
}
package de.scrum_master.aspect;
import de.scrum_master.app.Hello;
public aspect LoggingAspect {
pointcut methodExecuted() : execution(* *(..)) && #annotation(Hello);
Object around(): methodExecuted() {
System.out.println("Hello");
Object result = proceed();
System.out.println("world!");
return result;
}
}
Console log:
Hello
How are you?
world!

AspectJ designator #args() not working in Spring AOP

I am learning Spring and I searched a lot about how to properly use #args() AspectJ designator but I am still not clear completely. What I know about it is that it limits joint-point matches to the execution of methods whose arguments are annoted with the given annotation types. This does not seem to work in my case.
So here goes my files:
Human.java
#Component
public class Human {
int sleepHours;
public int sleep(String sleepHours) {
this.sleepHours = Integer.parseInt(sleepHours);
System.out.println("Humans sleep for " + this.sleepHours + " hours.");
return this.sleepHours+1;
}
}
Sleepable.java - Sleepable annotation
package com.aspect;
public #interface Sleepable {
}
SleepingAspect.java - Aspect
#Component
#Aspect
public class SleepingAspect {
#Pointcut("#args(com.aspect.Sleepable)")
public void sleep(){};
#Before("sleep()")
public void beforeSleep() {
System.out.println("Closing eyes before sleeping");
}
#AfterReturning("sleep()")
public void afterSleep() {
System.out.println("Opening eyes after sleep");
}
}
MainApp.java
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Human human = (Human) context.getBean("human");
#Sleepable
String sleepHours = "8";
human.sleep(sleepHours);
}
}
Output
Humans sleep for 8 hours.
Expected Output
Closing eyes before sleeping
Humans sleep for 8 hours.
Opening eyes after sleep
You have several mistakes in your code:
Spring AOP can only intercept method calls upon Spring components. What you are trying to intercept is an annotation on a local variable. Not even the much more powerful AspectJ can intercept anything concerning local variables, only read/write access to class members. Thus, what you are trying to do is impossible. And by the way, it is bad application design. Why would anyone want to rely on method internals when trying to apply cross-cutting behaviour? Method internals are subject to frequent refactoring. Suggestion: Put your annotation on method public int sleep(String sleepHours).
Your annotation is invisible during runtime because your forgot to add a meta annotation like #Retention(RetentionPolicy.RUNTIME).
#args is the wrong pointcut type. It captures method arguments the types of which are annotated. You want to use #annotation(com.aspect.Sleepable) instead.
I think you should not try a copy & paste cold start with Spring AOP but read the Spring AOP manual first. Everything I explained here can be found there.
Update: So according to you comments you were just practicing and trying to make up an example for #args(). Here is one in plain AspectJ. You can easily use it in similar form in Spring AOP. The #Before advice shows you how to match on an argument with an annotation on its class, the #After advice also shows how to bind the corresponding annotation to an advice argument.
Annotation + class using it:
package com.company.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {}
package com.company.app;
#MyAnnotation
public class MyClass {}
Driver application:
package com.company.app;
public class Application {
public static void main(String[] args) {
new Application().doSomething(new MyClass(), 11);
}
public String doSomething(MyClass myClass, int i) {
return "blah";
}
}
As you can see, here we use the annotated class MyClass in a method argument. This can be matched with #args() in the following aspect.
Aspect:
package com.company.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import com.company.app.MyAnnotation;
#Aspect
public class MyAspect {
#Before("#args(com.company.app.MyAnnotation, ..)")
public void myBeforeAdvice(JoinPoint thisJoinPoint) {
System.out.println("Before " + thisJoinPoint);
}
#After("#args(myAnnotation, ..)")
public void myAfterAdvice(JoinPoint thisJoinPoint, MyAnnotation myAnnotation) {
System.out.println("After " + thisJoinPoint + " -> " + myAnnotation);
}
}
Console log:
Before call(String com.company.app.Application.doSomething(MyClass, int))
Before execution(String com.company.app.Application.doSomething(MyClass, int))
After execution(String com.company.app.Application.doSomething(MyClass, int)) -> #com.company.app.MyAnnotation()
After call(String com.company.app.Application.doSomething(MyClass, int)) -> #com.company.app.MyAnnotation()
Of course, call() joinpoints are unavailable in Spring AOP, so there you would only see two lines of log output.

How to write AspectJ AOP expression for this method?

I have tried many combinations but I am unable to invoke callback before execution of following method
#SomeAnnotation(...)
#Override
public void someMethod(Serializable id) {
}
I have tried many combinations similar to
#Before("execution(#com.full.name.of.SomeAnnotation * com.full.name.of.Class.someMethod(java.io.Serializable))")
public void beforeMethod() {
System.out.println("I am here.");
}
If I write a more generic expession, it hits the beforeMethod but I am unable to target a single specific method. What am I missing here?
Okay guys, let me prove that the pointcut actually works as written by the original poster Haris Hasan.
Sample annotation:
package com.full.name.of;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface SomeAnnotation {
int id();
String name();
}
Sample class using the annotation:
package com.full.name.of;
import java.io.Serializable;
public class Class {
#SomeAnnotation(id = 1, name = "John Doe")
public void someMethod(Serializable s) {}
public static void main(String[] args) {
new Class().someMethod("x");
}
}
Sample aspect with Haris Hasan's exact pointcut:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class SampleAspect {
#Before("execution(#com.full.name.of.SomeAnnotation * com.full.name.of.Class.someMethod(java.io.Serializable))")
public void yetAnotherPointcut(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
Console output:
execution(void com.full.name.of.Class.someMethod(Serializable))
Copy and paste everything exactly as is, then run it. Quod erat demonstrandum.
The expression below should work:
#Before("com.full.name.of.Class.someMethod(..) && args(java.io.Serializable)")
Correction by kriegaex:
#Before("execution(* com.full.name.of.Class.someMethod(*)) && args(java.io.Serializable)")
A few points worth considering: Is the method being advised public? Are you using cglib proxies or jdk proxies? Does your class implement any interface and is the method being advised declared in the interface contract?
Also do take a look at what spring doc has to say about writing good pointcuts
Hope this helps.
Why do you not just use
#Before("execution(* com.full.name.of.Class.someMethod(java.io.Serializable))")
That specifically targets the method you want, you do not need to put the annotation to specify the target

AspectJ - Multiple #annotation Pointcut

I can't make a pointcut with "||" operator and multiple annotations. I'm trying to create a Pointcut for some JBehave annotations (#Given, #Then, #When).
This works fine:
#Pointcut("#annotation(given)")
public void jBehaveGivenPointcut(Given given) { }
and if I create and advice arount it, it also works.
What would be the syntax for making the Pointcut for the three annotations? Since I had used the logical OR operator in other pointcuts I would assume it is something like:
#Pointcut("#annotation(given) || #annotation(then) || #annotation(when) ")
public void jBehaveGivenPointcut(Given given, Then then, When when) { }
but it doesn't work, I get an inconsist binding exception. I tried other combinations but couldn't find the one that does the trick.
What you want cannot be done this way because, as the error message says, the annotation binding is inconsistent: You cannot bind all three annotations at the same time because they are in (possibly mutually exclusive) OR parts of the pointcut, i.e. usually only of of them can be bound (unless you assign multiple annotations to the same method). Your expectation might be that AspectJ can deal with this inconsistency by just assigning null to the other two if one is bound, but this is not how the compiler works right now.
I can offer a workaround which involves reflection instead of using the #annotation() binding.
Driver application:
package de.scrum_master.app;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
public class Application {
public static void main(String[] args) {
doGiven("foo");
doSomething("bar");
doWhen(11);
doSomethingElse(22);
doThen();
}
#Given("an input value") public static void doGiven(String string) {}
#When("I do something") public static void doWhen(int i) {}
#Then("I should obtain a result") public static boolean doThen() { return true; }
public static void doSomething(String string) {}
public static void doSomethingElse(int i) {}
}
Aspect:
package de.scrum_master.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
#Aspect
public class JBehaveInterceptor {
#Pointcut("execution(#org.jbehave.core.annotations.* * *(..))")
public void jBehavePointcut() {}
#Before("jBehavePointcut()")
public void jBehaveAdvice(JoinPoint.StaticPart thisJoinPointStaticPart) {
Method method = ((MethodSignature) thisJoinPointStaticPart.getSignature()).getMethod();
for (Annotation jBehaveAnnotation : method.getAnnotations()) {
if (jBehaveAnnotation.annotationType().getPackage().getName().equals("org.jbehave.core.annotations"))
System.out.println(thisJoinPointStaticPart + " -> " + jBehaveAnnotation);
}
}
}
As you can see, the pointcut only intercepts methods annotated by org.jbehave.core.annotations.* which narrows down pointcut matching considerably - to more than just #Given, #When, #Then, but maybe that is even what you want because JBehave offers more annotations than just those.
In the advice we loop over all method annotations because there might be more than just the JBehave ones. If any annotation package name matches the corresponding JBehave package, we do something (in this case print the annotation to standard output).
I hope this solves your problem. I cannot extend the AspectJ language for you, this is the best I can think of. Anyway, this yields the following output:
execution(void de.scrum_master.app.Application.doGiven(String)) -> #org.jbehave.core.annotations.Given(priority=0, value=an input value)
execution(void de.scrum_master.app.Application.doWhen(int)) -> #org.jbehave.core.annotations.When(priority=0, value=I do something)
execution(boolean de.scrum_master.app.Application.doThen()) -> #org.jbehave.core.annotations.Then(priority=0, value=I should obtain a result)
This works:
#Component
#Aspect
#RequiredArgsConstructor
public class PermissionAspect {
#Pointcut("#annotation(org.springframework.web.bind.annotation.GetMapping)")
public void getMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.PostMapping)")
public void postMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.PutMapping)")
public void putMapping(){}
#Pointcut("#annotation(org.springframework.web.bind.annotation.DeleteMapping)")
public void deleteMapping(){}
#Around("(execution(* xx.*.controller.*.*(..))) && (getMapping() || postMapping() || putMapping() || deleteMapping())")
Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

Categories