3

I am working on a logging aspect which need to intercept all the classes and methods annotated with a custom annotation.

Below is custom annotation class which can be annotated on class and methods:

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Loggable {
    LogLevel value();
}

I am using these pointcut expressions to intercept methods and classes with annotation @Loggable, which is working for all the simple classes but does not work for classses which extend or implement.

//Works for annotation @Loggable on class level
@Pointcut("execution(* *(..)) &&  within(@com.logger.Loggable *)")
public void classAnnotationLogger() {
}

//Working for methods annotated with @Loggable
@Before(value = "@annotation(loggable)", argNames = "jp, loggable")
public void logBeforeAdvice(JoinPoint jp, Loggable loggable) {
  ..
  ..
}

Below is code for super class

@Component
@Loggable(LogLevel.INFO)
public abstract class Processor{
  public void process(){
      readProcess();
  }

  public abstract void readProcess();
}

Below is code for subclass

@Service
@Loggable(LogLevel.INFO)
public class MyServiceProcessor extends Processor {

  @Override
  public void readProcess(){
    ...
    ...
  }
}

In the application readProcess() is called by doing

Processor processor = applicationContext.getBean(MyServiceProcessor.class);
processor.readProcess();

Even though I have @Loggable on Processor and MyServiceProcessor, when readProcess is called the advice is not being invoked.

But advice is invoked for process() and not readProcess.

How do I write the pointcut expression which also intercepts the call to any subclass methods, when annotation @Logabble is applied on any class or method?

dimo414
  • 47,227
  • 18
  • 148
  • 244
user1300877
  • 169
  • 1
  • 3
  • 11

1 Answers1

1

Well, first of all this

@Pointcut("execution(* *(..)) &&  within(@com.logger.Loggable *)")
public void classAnnotationLogger() {}

is just a pointcut and not an advice, so it does not do anything unless you also have an advice actually using this pointcut. You have not posted such an advice, so I can only speculate.

Secondly, you have not provided any sample code which would be triggered by

@Before(value = "@annotation(loggable)", argNames = "jp, loggable")
public void logBeforeAdvice(JoinPoint jp, Loggable loggable) {}

at all, i.e. no annotated method. Your sample code only shows annotated classes.

As for the @Loggable annotation on the subclass, it should not be necessary because its base class already carries the same annotation and the annotation is @Inherited. This works for annotations on classes, but not for annotations on methods or interfaces, see my other answer for an explanation and a possible workaround.

This example of yours should actually work, I cannot see a reason why it would not:

Processor processor = applicationContext.getBean(MyServiceProcessor.class);
processor.readProcess();

But this internal call to readProcess() (equivalent to this.readProcess()) will not work:

public void process() {
    readProcess();
}

This is because Spring AOP is a proxy-based "AOP lite" framework relying on JDK dynamic proxies (for interfaces) or CGLIB proxies (for classes). But calls to this.someMethod() are not routed through proxies of any type so they cannot be intercepted by Spring aspects. This is documented behaviour. If you want to overcome this limitation and apply aspects to internal method calls as well, please use full-blown AspectJ as documented here.

Community
  • 1
  • 1
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Hi, Thanks for showing interest. In my code I have advice for pointcut classAnnotationLogger. I understood that `this.someMethod()`wont work with standard spring AOP. **The problem here is just annotating on overridden method readProcess() in subclass method is not working, but annotating on class level, the process() method is being logged. Is there anyway I can do logging for the overridden method which has @Loggable annotation?** – user1300877 Jun 29 '15 at 09:18
  • Odds are bad here because `@Inherited`, as I said, only works for classes, not for interfaces or methods. This is a JVM/javac limitation rather than an AspectJ one. Maybe Spring can do some magic there, but from an AspectJ perspective this will not work. So either you annotate your subclasses manually or use APT in order to add annotations before compilation or you resort to class-level annotations plus method name matching, annotation parameters or other differentiating factors. – kriegaex Jun 29 '15 at 12:17
  • I am using compile time weaving of aspects to fix this. – user1300877 Jul 08 '15 at 10:15
  • Can u please look into this new issue [http://stackoverflow.com/questions/31290852/autowired-dependencies-are-null-in-compile-time-weaving-of-aspect-class] I am facing. I really appreciate for sharing knowledge on aspects. – user1300877 Jul 08 '15 at 11:08