Create a pointcut based on annotation parameters - java

I'm trying to create a pointcut based on the parameter of an annotaion
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface MyAnnotation {
Class<? extends ABC> style() default A.class;
}
And the pointcut I'm currently using is:
#Pointcut("execution(#com.something.MyAnnotation * *(..))")
public void dummyMethod() {
}
#Around("method()")
public Object actualFunc(ProceedingJoinPoint joinPoint) throws Throwable {
//stuff
}
But that unfortunately activates on all values of style.

Obviously you could check in the advice if the advised method had the annotation value you are looking for, but that is less than ideal (it is a runtime check). In your case you can just use the syntax:
#Pointcut("execution(#com.something.MyAnnotation(style=B.class) * *(..))")
There is a little bit of info here on annotation value matching: https://eclipse.org/aspectj/doc/released/README-160.html

Related

How to write Pointcut expression for Spring AOP to weave meta-annotation in any level of deepness?

If I have Annotation:
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Loggable {
enum LogLevel {
ERROR, WARN, INFO, DEBUG, TRACE;
}
LogLevel value() default DEBUG;
}
and trying to use it as target marker of Spring AOP aspect joinpoint marker, it works well for some aspect, weaved by that composite pointcut:
#Aspect
class LoggableAspect {
#Pointcut("within(#Loggable *)")
void aClass() {}
//#Pointcut("#annotation(Loggable)") - in straight-forward case it works too!
#Pointcut("execution(#Loggable * *(..))")
void method() {}
#Around("method() || aClass()")
Object aroundAdvice(ProceedingJoinPoint pjp) {...}
}
In other words, it works well as "straight-forward" annotation usage, when I write code like this:
#Component
public class Abc {
#Loggable
public String method(int x) {
return "string: " + x;
}
}
But in case of META-annotation...:
#Loggable(INFO)
#Retention(RUNTIME)
public #interface Log {
}
...it doesn't work for example, in that code:
#Component
public class Abc {
#Log // doesn't work! :(
public String method(int x) {
return "string: " + x;
}
}
Certainly, I can write yet another pointcut for that particular case of 2-level deepness:
//...
#Pointcut("execution(#(#Loggable *) * *(..))")
void metaMethod() {}
#Around("method() || metaMethod() || aClass()")
Object aroundAdvice(ProceedingJoinPoint pjp) {...}
and it will work, but I want universal solution, working on any level of deepness - 3, 4, 5... Is it possible for that style of AOP?
P.S. That issue:
execution(public * ((#Transactional *)+).*(..))
looks like exactly right solution, but unfortunately, it doesn't work in my case. I think, it's possible only as AspectJ solution (in *.aj files) - not for Spring AOP. Am I right?..
The correct answer to your question, even though you might not like it, is: Neither Spring AOP as a syntactical subset of AspectJ nor native AspectJ provide a dedicated syntax to achieve what you wish to do. The best you can actually do is to use a nested syntax like you suggested yourself and use a reasonable number of nesting levels.
In my answer here you find solutions for both class- and method-level meta annotations.
Update: You misunderstood what
execution(public * ((#Transactional *)+).*(..))
means, even though the answer you linked to explains it:
Matches the execution of any public method in a type with the Transactional annotation, or any subtype of a type with the Transactional annotation.
So this syntax is about class inheritance, not about meta annotation nesting. It means that if you have #Transational class Parent, it would match class Child extends Parent and class Grandchild extends Child etc. The syntax should also work in Spring AOP, but that does not solve your problem.

Create Pointcut for Spring AOP annotation to put annotation on class and execute advice on every method in class

I want to have annotation on class level that will execute advice on every method in annotated class.
Is that even possible.
Example: I would like to annotate OmniDemoService with #DoSomethingForMe and I want both method1 and method2 to log "look at me" before execution
This example is not working and I don't know why. When I transform Pointcut to Around and just use it with annotation (also change annotation ElementType to method) everything is working on method level.
So I think it is wrong defined Pointcut.
Annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface DoSomethingForMe {
}
Advice:
#Aspect
#Component
public class DoSomethingForMeAdvice {
private static final Logger logger = LoggerFactory.getLogger(DoSomethingForMeAdvice.class);
#Pointcut("execution(public * *(..)) && #annotation(DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void acquireExecution() {
logger.info("look at me");
}
}
Usage:
#Service
#DoSomethingForMe
public class OmniDemoService {
private static final Logger logger = LoggerFactory.getLogger(OmniDemoService.class);
public void method1() {
logger.info("---1---");
}
public void method2() {
logger.info("---2---");
}
}
Your issue is that you are confusing pointcut definition with advices.
Pointcut is aiming, advice performs the actual WhateverYouWantToBeExecuted. Like for example
#Pointcut("#annotation(com.omnidemo.advice.DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void logMethodCall(JoinPoint jp) {
String methodName = jp.getSignature().toShortString();
logger.info("Executing: " + methodName);
}
Solution for the problem is to use within for pointcut
#Pointcut("#within(DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void acquireExecution() {
logger.info("look at me");
}
Solution provided by #J Asgarov in the comments
Check out what the AspectJ quick reference says about #annotation():
any join point where the subject has an annotation of type
SomeAnnotation
You used #annotation(DoSomethingForMe) but the "subject" of a method execution is a method. So that would mean any method annotated #DoSomethingForMe.
Use #this(DoSomethingForMe) or #target(DoSomethingForMe).
Thanks to kriegaex for pointing out that #this and #target must be evaluated at runtime, which would pollute the codebase a lot (i.e. check in every method). So the next approach is better:
If you check the AspectJ manual section about type patterns you will see that you can annotate the type directly. Please also remember to use use fully qualified class names. So that would be:
execution(public * (#com.path.DoSomethingForMe *).*(..))
Also, if you have such a simple pointcut and you don't need to reuse it, I think you can drop the additional method and just use it in the advice directly:
#Before("execution(public * (#com.path.DoSomethingForMe *).*(..))")
which says: "before the execution of any public method of a type annotated with #com.path.DoSomethingForMe", where "before the execution of a method" means "inside the method, at the beginning".
Alternatively, if this pointcut looks a bit too complicated for you, you can separate annotation matching and method matching like this, as suggested by J Asgarov in his comment:
#Before("execution(public * *(..)) && #within(com.path.DoSomethingForMe)")

Have a common base type for all my custom annotations

So, I have created several custom annotations:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Foo {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Bar {
}
Those annotations are used in my functions:
public class Worker {
#Foo
public void doTaskOne() {...}
#Bar
public void doTaskX() {...}
...
}
I want to use java reflection to check if certain annotation is declared in one method.
for (Method m : methods) {
if (m.isAnnotationPresent(Foo.class)) {
...
} else if (m.isAnnotationPresent(Bar.class)) {
...
}
}
The problem is that since in Java, custom annotation #interface is not able to be extended. I mean this is illegal:
public #interface Bar extends MyBaseAnnotation{
}
That's I am not able to have a base #interface for all my custom annotation class Foo and Bar. So, if I have a new custom annotation created, I need to add more else if condition in above method checking code, which sucks! Is there anyway to get rid of this problem? What I want to achieve is to generalize my method checking code to :
for (Method m : methods) {
if (m.isAnnotationPresent(MyBaseAnnotation.class)) {
...
}
}
How to achieve it?
You can annotate your custom annotations with a base custom annotation, like composed annotations do.
Instead of:
public #interface Bar extends MyBaseAnnotation{
}
use:
#MyBaseAnnotation
public #interface Bar {
}
Assuming that
#Parent
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface Foo {}
#Parent
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#interface Bar {}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.ANNOTATION_TYPE)
#interface Parent {}
and there is a method
public static boolean isAnnotationPresent(Method method, Class<? extends Annotation> parentAnnotation) throws NoSuchMethodException {
for (Annotation methodAnnotation : method.getDeclaredAnnotations()) {
if (methodAnnotation.annotationType().isAnnotationPresent(parentAnnotation)) {
return true;
}
}
return false;
}
you can do
isAnnotationPresent(m, Parent.class)
You got it right: there is no inheritance between annotation types in Java. You could make your own rules, though. By saying "if annotation B has annotation A over it, then B extends A", you define the rule that you will follow while using reflection.

aspect is not invoked

I've got a simple aspect that supposed to set the value of class fied, that has annotation #GuiceInject.
Originally I have this
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository repository;
And I expect to get similar to this
private IRacesRepository repository = GuiceInject.getInstance(IRacesRepository.class);
And here is my aspect
public aspect InjectionAspect {
Object around(): get(#GuiceInject * *) {
System.out.println(thisJoinPointStaticPart);
// instantiate object as it supposed to be null originally
return GuiceInjector.getInstance(thisJoinPoint.getTarget().getClass());
}
}
As far as I understand - I am new to AOP - it supposed to replace get invokations of the field with the code in aspect.
It compiles fine, but when I run the application - nothing happens. I get NullPointerException for readRaces method as it stays null so aspect did not work.
My main class looks like this
public class Example {
#GuiceInject(module=RepositoryModule.class)
private IRacesRepository racesRepository;
private void execute() {
System.out.println("List of races: " + racesRepository.readRaces());
}
public static void main(String[] args) {
new Example().execute();
}
}
What is the problem? Annotation has this definition
#Target(ElementType.FIELD)
// make annotation visible in runtime for AspectJ
#Retention(RetentionPolicy.RUNTIME)
public #interface GuiceInject {
Class<? extends AbstractModule> module();
}
Please try to redefine pointcut syntax as
Object around(): get(#package.subpackage.GuiceInject * *.*)
Correct field signature must specify the type of the field, the declaring type, and name. If your annotation is in different package, it should be fully qualified.

how to get the Annotated object using aspectJ

I have an annotation like this:
#Inherited
#Documented
#Target(value={ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Restful {
}
I annotated this class like this:
#Restful
public class TestAspect {
public String yes;
}
I have a pointcut like this:
#Pointcut("#annotation(com.rest.config.Restful)")
public void pointCutMethod() {
}
I tried:
#Before("pointCutMethod()")
public void beforeClass(JoinPoint joinPoint) {
System.out.println("#Restful DONE");
System.out.println(joinPoint.getThis());
}
But getThis() returns null.
Basically I am trying to get that Object instance of TestAspect. How do I do it? Any clue? any help would be really appreciated.
Thanks in advance
With your annotation placed on the type only and the pointcut #annotation(com.rest.config.Restful) you are only going to match the static initialization join point for your type. As we can see if you use -showWeaveInfo when you compile (I slapped your code samples into a file called Demo.java):
Join point 'staticinitialization(void TestAspect.<clinit>())' in
Type 'TestAspect' (Demo.java:9) advised by before advice from 'X' (Demo.java:19)
When the static initializer runs there is no this, hence you get null when you retrieve it from thisJoinPoint. You haven't said what you actually want to advise but let me assume it is creation of a new instance of TestAspect. Your pointcut needs to match on execution of the constructor for this annotated type:
// Execution of a constructor on a type annotated by #Restful
#Pointcut("execution((#Restful *).new(..))")
public void pointcutMethod() { }
If you wanted to match methods in that type, it would be something like:
#Pointcut("execution(* (#Restful *).*(..))")

Categories

Resources