Use load-time weaving, pure AspectJ.
We have 2 annotations #Time and #Count, and a few annotated methods.
#Time (name="myMethod1Time")
#Count (name="myMethod1Count")
public void myMethod1(){..};
#Time (name="myMethod2Time")
public void myMethod2(){..};
#Count (name="myMethod3Count")
public void myMethod3(){..};
Now I am defining my own around aspect for myMethod1 which has multiple annotations:
// multiple annotations, not working
#Around("#annotation(time) && #annotation(count))
public Object myAspect(Time time, Count count) {..}
This doesn't work. However, capture method myMethod2 works fine with a single annotation:
// single annotation, is working
#Around("#annotation(time))
public Object myAnotherAspect(Time time) {..}
I want to capture only methods with both Time and Count annotations present in their signature, and I want to use the annotation value. Anyone know how to achieve this?
Maybe combine 2 pointcuts like:
#Around("call(#Time * *(..)) && call(#Count * *(..))");
Related
I have created a java 17 repeatable annotation and want to create an aspect around the method containing the annotation is invoked. This seems to work when method is annotated once but fails to invoke when I have repeatable annotation. I am using aspectjrt version 1.9.7. Am I doing something wrong or aspect doesn't support repeatable annotations? Any workaround for the same?
Annotation class ->
#Repeatable(Schedules.class)
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
The repeatable class ->
#Retention(value = RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Schedules {
Schedule[] value();
}
The aspect class ->
#Aspect
#Component
#Slf4j
public class Aspect {
#Around("#annotation(Schedule)")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Schedule filters = method.getAnnotation(Schedule.class);
//Business Logic
return joinPoint.proceed();
}
}
This actually is not an AOP problem, but you need to understand how repeatable annotations work in Java: If the annotation appears multiple times on the annotated element, it is not represented as a single annotation anymore but as an array of annotations in the value() of the annotation type mentioned in #Repeatable, i.e. in your case #Schedules (plural "s"!).
For your aspect, it means that you need two pointcuts, one for the single-annotation case and one for the multi-annotation one. I am suggesting to factor out the common advice code into a helper method of the aspect which always takes an array of the repeatable annotations, then just pass the value of the wrapper annotation on in one case and a one-element array in the other case.
Feel free to ask follow-up questions, if anything is unclear. But it should be straightforward, almost trivial.
Resources:
https://docs.oracle.com/javase/tutorial/java/annotations/repeating.html
https://dzone.com/articles/repeatable-annotations-in-java-8-1
P.S.: You should learn about how to bind annotations to advice method parameters:
#Around("#annotation(schedule)")
public Object traceSingle(ProceedingJoinPoint joinPoint, Schedule schedule) throws Throwable
// ...
#Around("#annotation(schedules)")
public Object traceMultiple(ProceedingJoinPoint joinPoint, Schedules schedules) throws Throwable
I have methods annotated as follows:
#Action(actionType=<..>)
public void doSomeAction() {
...
}
Now, I would like to invoke one of such annotated methods with given actionType from another flow in another class.
In order to do this, I found the approaches as mentioned in the following SO posts:
How to run all methods with a given annotation?
Java seek a method with specific annotation and its annotation element
But in some cases, such methods may accept arguments as follows:
#Action(actionType='SEND_NOTIFICATION')
public void sendReport(String email) {
...
}
In this regard, I am not able to understand, how to check if the annotated method has any params and invoke such methods with required params.
I need a pointcut for methods in classes annotated with #X or methods annotated with #X. I also need the annotation object. If both the class and the method are annotated I prefer to get the method annotation as argument.
I tried the following, which creates an "inconsistent binding" warning. (Why not just set them null?)
#Around("#annotation(methodLevelX) || #within(classLevelX)")
public Object advise(ProceedingJoinPoint pjp, X methodLevelX, X classLevelX)
The following creates a "ambiguous binding of parameter(s) x across '||' in pointcut" warning. (Which does not necessarily make sense in my opinion: Why not bind the first short circuited evaluation?)
#Around("#annotation(x) || #within(x)")
public Object advise(ProceedingJoinPoint pjp, X x)
Splitting the previous attempt in two naturally results in two method calls if class and method annotations are present.
I know I could just get the method and class with reflection and my desired annotation with a pointcut like this:
#Around("#annotation(com.package.X) || #within(com.package.X)")
But I'd prefer not to.
Is there any "one pointcut, one method, one annotation argument", solution for my requirement that does not require reflection?
Not quite, but almost. You will need two pointcuts, two advices, but you can delegate the work to a single method. Here's how it would look like:
#Aspect
public class AnyAspectName {
#Pointcut("execution(#X * *.*(..))")
void annotatedMethod() {}
#Pointcut("execution(* (#X *).*(..))")
void methodOfAnnotatedClass() {}
#Around("annotatedMethod() && #annotation(methodLevelX)")
public Object adviseAnnotatedMethods(ProceedingJoinPoint pjp, X methodLevelX)
throws Throwable {
return aroundImplementation(pjp, methodLevelX);
}
#Around("methodOfAnnotatedClass() && !annotatedMethod() && #within(classLevelX)")
public Object adviseMethodsOfAnnotatedClass(ProceedingJoinPoint pjp, X classLevelX)
throws Throwable {
return aroundImplementation(pjp, classLevelX);
}
public Object aroundImplementation(ProceedingJoinPoint pjp, X annotation)
throws Throwable {
return pjp.proceed();
}
}
Note that besides splitting apart the #annotation() and #within() pointcuts, I added restrictions to the resulting pointcuts so that they aren't too broad. I suppose you want method execution join points, so I added the needed pointcut expressions that would restrict it to method execution. They are matching
execution of any method annotated with #X with any return type in any class being in any package for the first advice
execution of any method with any return type in any class annotated with #X for the second.
Further restricting #within(X) and #annotation(X) comes in handy, because #within(X) by itself would match
any join point where the associated code is defined in a type with
an annotation of type X
which would include method-execution, method-call, constructor-execution, constructor-call, pre-initialization, static initialization, initialization, field set, field get, exception-handler, lock type join points (not all join points are valid for around advices though). Similarly, #annotation(X) by itself would mean
any join point where the subject has an annotation of type X
which could also mean most of the previously mentioned join points, depending on the target type of your annotation.
I have this annotation defined:
public #interface MyAspectWeaved { }
And this aspect:
aspect MyAspect {
declare #method : * *(..) : #MyAspectWeaved;
// Other pointcuts and advices which work without issues
// ...
}
I'm basically trying to mark all methods which could have been affected by MyAspect. The rest of the aspect works fine but for some reason no methods are annotated with #MyAspectWeaved. Google searches and rereading the documentation didn't give me any clues. I'm not very familiar with AspectJ so I assume that either I'm missing something obvious or my understanding of declare #method is incorrect. Anyone see what's wrong?
I'm using AspectJ 1.6.12.
Thanks
You have to change the annotation to:
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAspectWeaved {
}
I'd like to match a method like this:
#Foo
public void boo(#Baz Bar bar) { ... }
Basically:
the method has a #Foo annotation (which I match with execution(#Foo * *(..)) && #annotation(foo)),
can have a variable amount of parameters,
and one of them should have a #Baz annotation,
I need to further work with that annotated argument (bar).
If a method has a #Foo annotation but is missing a #Baz annotation, I want to get an error as early as possible, if possible when weaving and not at runtime.
How can I do that?
public pointcut annArg(): execution(#Foo * *(.., #Baz (*),..));
declare error :execution(#Foo * *(..))&&!annArg() :"error";
Unfortunatly it is impossible to grab matched argument by args(..,arg,..). But you can use thisJoinPoint.getArgs() and reflection API to get the annotated argument. Or if you know the position of the argument you can use something like args(..,arg);