Spring AOP - Invoking advice from catch block

2019-02-18 06:20发布

Purpose: Send an email to admin every time an exception occurs while executing business logic.

Till now I have come across "throwing advice" which is fine and gets executed when an exception is raised from the target method.

This could have worked well for me but I have to do some additional processing in terms of setting request attribute and next page. I don't think it would be a good idea to share the objects from target class with advice by making those objects static. The code scenario is shown below:

try{
   //normal processing
} catch (AuthenticationException ae) {
   ae.printStackTrace();
   req.setAttribute("msg", ae.getMessage());

   //execute advice at this point

   return loginPage;
}

Pls. see the point where I want to execute the advice and suggest solution accordingly.

Best Regards

4条回答
男人必须洒脱
2楼-- · 2019-02-18 06:21

As far as my understanding says why AuthenticationException is thrown in business logic code.If you really want to use aop keep urs business code far from cross cutting concern. In yours case you can take out AuthenticationException code from business logic.Before entering in business logic operation apply aop.for example

public class SecurityInterceptor extends HandlerInterceptorAdapter{



    //before the actual handler will be executed
    public boolean preHandle(HttpServletRequest request, 
        HttpServletResponse response, Object handler)
        throws Exception {


        // check here possible case for exception from request .If exception throw yours custom exception before executing business logic


    }

    //after the handler is executed
    public void postHandle(

        }
    }
}

Custom exception

public class Handler
        implements HandlerExceptionResolver
    {

        public Handler()
        {
        }

        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
        {
            if(ex instanceof AuthenticationException))
            {
                 ModelAndView test = new ModelAndView("errorpage1jsppage");
return test;
            } else
            if(ex instanceof ErrorType2Exception))
            {
                 ModelAndView test1 = new ModelAndView("errorpage2jsppage");
return test1
            }
        }
    }
查看更多
冷血范
3楼-- · 2019-02-18 06:26

ok, after going through reference books like Spring in Action, I came to know that there is no way by which we can invoke spring advices at arbitrary points in our java code. Spring in Action book recommends to have a look at AspectJ for fine grain control over point cuts.

In order to avoid adding AspectJ, I came across the following solution which could help others and save their precious time:

1) Use an Around advice for the method where you want to invoke advice only when an exception occurs. Like in my case, I want to send email notification when an exception occurs and we get out of the catch block. Basically I wanted to do some processing in the catch block before invoking advice.

2) When using Around advice, we can read the member variables of target object as method arguments. If you want to share some data with the advice, it is also one of the way. In my case I wanted details from the target object about email subject and body.

Code for around advice:

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class NotificationAdvice implements AfterReturningAdvice  {
    public void afterReturning(Object returnValue, Method method,
            Object[] args, Object target) throws Throwable {
            System.out.println(returnValue);
            System.out.println(method.getName());
            System.out.println(args[0]);
            System.out.println(((target)target).flag);
    }
}

Do share your feedback/queries on this approach.

查看更多
我只想做你的唯一
4楼-- · 2019-02-18 06:36

AFAIK there is no pointcut expression for this...

You should consider configuring your logger to do what you need. Replace ae.printStackTrace(); by something like logger.warn(ae) (to print stacktraces to the console is quite bad practice anyways), and configure one of the email-sending appenders of your logging tool, e.g. log4j's or logback's SMTPAppender. Additionally, to make your configuration easier, you may use a business-dedicated logger.

If you really want to use aspects here, I think you'll have to expect all your business exceptions to bubble up at least one method, in order to use afterThrowing advices.

查看更多
\"骚年 ilove
5楼-- · 2019-02-18 06:43

I know you want to avoid full AspectJ as opposed to Spring AOP. (BTW, I wonder why many people are so afraid of it.) Anyway, in AspectJ is is really easy to intercept exception handler execution (=catch blocks) by means of the handler() pointcut. There is one limitation though: It only works with before() advice, not after() or around(). This is due to compiler limitations. Look at JVM byte code of exception handlers and you will see that there is no way to detect the end of a handler block. Anyway, because the concept is related to the original question, I want to show here how it is done. I have created a little driver application and a very simple aspect:

import java.util.Random;
import javax.naming.AuthenticationException;

public class Application {
    public static void main(String[] args) {
        Application app = new Application();
        System.out.println(app.foo(1, "two", 3d));
        System.out.println(app.bar("one", 2d, 3));
        System.out.println(app.zot(1d, 2, "three"));
    }

    public String foo(int i, String string, double d) {
        try {
            if (new Random().nextBoolean())
                throw new AuthenticationException("wrong password");
        }
        catch (AuthenticationException e) {
            return "return value from catch block";
        }
        return "normal return value";
    }

    public String bar(String string, double d, int i) {
        try {
            if (new Random().nextBoolean())
                throw new IllegalArgumentException("I don't like your arguments");
        }
        catch (IllegalArgumentException e) {
            return "return value from catch block";
        }
        return "normal return value";
    }

    public String zot(double d, int i, String string) {
        try {
            int n = 2/0;
        }
        catch (Throwable t) {
            return "return value from catch block";
        }
        return "normal return value";
    }
}

As you can see, methods foo and bar throw exceptions based on random values in ca. 50% of all cases, whereas zot always throws a division by zero exception. So the output will differ from run to run.

So how do we find out what is going on if all exceptions are silently swallowed and not logged? Like so:

import java.util.logging.Logger;

public aspect ExceptionLoggingAspect {
    final Logger log = Logger.getLogger(ExceptionLoggingAspect.class.getName());

    before(Throwable t) : handler(Throwable+) && args(t) {
        log.warning(thisJoinPointStaticPart + "  ->  " + t);
    }
}

This is really simple and elegant and works throughout your application. Here is some test output:

Apr 6, 2013 12:15:43 PM ExceptionLoggingAspect ajc$before$ExceptionLoggingAspect$1$3d90b181
WARNING: handler(catch(AuthenticationException))  ->  javax.naming.AuthenticationException: wrong password
return value from catch block
Apr 6, 2013 12:15:43 PM ExceptionLoggingAspect ajc$before$ExceptionLoggingAspect$1$3d90b181
WARNING: handler(catch(IllegalArgumentException))  ->  java.lang.IllegalArgumentException: I don't like your arguments
return value from catch block
Apr 6, 2013 12:15:43 PM ExceptionLoggingAspect ajc$before$ExceptionLoggingAspect$1$3d90b181
WARNING: handler(catch(Throwable))  ->  java.lang.ArithmeticException: / by zero
return value from catch block

In the advice you can do more, e.g. access this and read/update some properties and so fort.

查看更多
登录 后发表回答