0

I need to handle all the exceptions that are thrown from public methods of class annotated with some annotation. I trying to use Spring AOP. This is my logger:

@Aspect
public class Logger {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut("@annotation(loggable)")
    public void isLoggable(Loggable loggable) {
    }

    @AfterThrowing(pointcut = "isLoggable(loggable)", throwing = "e")
    public void afterThrowing(Loggable loggable, Exception e) throws Throwable {
        log.error("AFTER", e);
    }

@Loggable is my annotation.

Then I've added @EnableAspectJAutoProxy annotation to my configuration class.

First I've tried to annotate some method that throws an exception. It works fine but how can I make this work for all public methods in class annotated with @Loggable annotation?

Dimitri Hautot
  • 438
  • 5
  • 12
Kirill
  • 1,540
  • 4
  • 18
  • 41
  • 2
    Is your `Logger` a `@Bean`... An `@Aspect` isn't a `@Component` and as such will not get component scanned. SO either register it as a bean or add `@Component` to have it detected or create a `@ComponentScan` to scan for all beans annotated with `@Aspect`. Either way your aspect has to become a bean else it doesn't exists and nothing will happen. – M. Deinum Mar 24 '17 at 07:43
  • 1
    Your solution with @around advice is correct. Try to debugging, check whenever advice is executed. – Marcin Tarka Mar 24 '17 at 07:47
  • @M.Deinum Thank you! It was very stupid mistake. – Kirill Mar 24 '17 at 07:55
  • @M.Deinum It works if method annotated with `@Loggable` but I need aspect to work for all public methods in annotated class – Kirill Mar 24 '17 at 09:08
  • @M.Deinum, I know you are always selfless, not greedy for reputation points and often provide the right answer as a simple comment. But this way the corresponding questions stay listed as unanswered. Would you mind writing short answers in such cases so as to enable the OPs to accept them? That would be great, thanks in advance. :-) – kriegaex Mar 24 '17 at 10:04
  • Update: See what just happened, @M.Deinum? Someone else has posted an answer even though the OP's problem has already been solved. – kriegaex Mar 24 '17 at 10:05

2 Answers2

4

You can create the aspect like this, where @LogMe is the annotation: @Pointcut("execution(@LogMe * *(..))") to match all the public methods.

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;


@Aspect
@Component
public class LogExecutionTime {

    private static final String LOG_MESSAGE_FORMAT = "%s.%s execution time: %dms";
    private static final Logger logger = LoggerFactory.getLogger(LogExecutionTime.class);

    @Pointcut("execution(@LogMe * *(..))")
    public void isAnnotated() {}

    /**
     * Method will add log statement of running time of the methods which are annotated with @LogMe
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("isAnnotated()")
    public Object logTimeMethod(ProceedingJoinPoint joinPoint) throws Throwable {
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();

      Object retVal = joinPoint.proceed();

      stopWatch.stop();

      logExecutionTime(joinPoint, stopWatch);

      return retVal;
    }

    private void logExecutionTime(ProceedingJoinPoint joinPoint, StopWatch stopWatch) {
      String logMessage = String.format(LOG_MESSAGE_FORMAT, joinPoint.getTarget().getClass().getName(), joinPoint.getSignature().getName(), stopWatch.getTotalTimeMillis());
      logger.info(logMessage.toString());
    }
}
Dimitri Hautot
  • 438
  • 5
  • 12
Mahesh Kumar
  • 138
  • 1
  • 9
4

A class annotated with @Aspect isn't a @Component so if you have component scanning enabled it won't be picked up. If there is no Aspect in your context, there is nothing use for AOP.

To fix this you can do 1 of 3 things:

  1. Put @Component next to the @Aspect
  2. Define the @Aspect as a @Bean
  3. Add an additional `@ComponentScan(includeFilter={@Filter(org.aspectj.lang.annotation.Aspect)}

Obviously option #1 is the easiest to do.

First I've tried to annotate some method method that throws an exception. It works fine but how can I make this work for all public methods in class annotated with @Loggable annotation?

You need to write a point cut that matches that. Something like the following should do the trick.

@Pointcut("public * ((@Loggable *)+).*(..)) && within(@Loggable *)")

together with

@Pointcut("@Loggable * *(..)")

Which will hit for annotated methods or public methods in annotated classes. This is inspired by the code from the AnnotationTransactionAspect from the Spring Framework.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224