Spring AOP - 从catch块调用建议

时间:2013-03-18 09:46:59

标签: java spring aop spring-aop

目的:每次执行业务逻辑时发生异常,都会向管理员发送电子邮件。

直到现在我遇到了“抛出建议”,这很好并且在从目标方法引发异常时被执行。

这可能对我有用,但我必须在设置请求属性和下一页方面做一些额外的处理。我认为通过使这些对象保持静态来与目标类共享对象并不是一个好主意。代码场景如下所示:

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

   //execute advice at this point

   return loginPage;
}

PLS。看看我想要执行建议并相应地建议解决方案。

最好的问候

4 个答案:

答案 0 :(得分:2)

我知道你要避免使用完整的AspectJ而不是Spring AOP。 (顺便说一句,我想知道为什么很多人都如此害怕它。)无论如何,在AspectJ中,通过handler()切入点很容易拦截异常处理程序执行(= catch块)。但有一个限制:它仅适用于before()建议,而不是after()around()。这是由于编译器的限制。查看异常处理程序的JVM字节代码,您将看到无法检测处理程序块的结尾。无论如何,因为这个概念与原始问题有关,所以我想在这里展示它是如何完成的。我创建了一个小驱动程序应用程序和一个非常简单的方面:

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";
    }
}

正如您所看到的,方法foobar会根据ca中的随机值抛出异常。占所有案例的50%,而zot总是抛出除零异常。因此输出将随着运行而不同。

那么,如果所有异常都被静默吞下并且没有记录,我们如何才能知道发生了什么?像这样:

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);
    }
}

这非常简单而优雅,适用于整个应用程序。这是一些测试输出:

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

在建议中,你可以做更多,例如访问this并阅读/更新一些属性等等。

答案 1 :(得分:1)

好的,在浏览了诸如 Spring in Action 之类的参考书之后,我开始知道我们无法在java代码中的任意点调用spring建议。 Spring in Action书籍建议看一下AspectJ,以便对切点进行细粒度控制。

为了避免添加AspectJ,我遇到了以下解决方案,它可以帮助其他人并节省宝贵的时间:

1)对于只在发生异常时要调用建议的方法,请使用Around建议。就像我的情况一样,我想在发生异常时发送电子邮件通知,然后我们离开catch块。基本上我想在调用建议之前在catch块中进行一些处理。

2)当使用Around建议时,我们可以将目标对象的成员变量读作方法参数。如果您想与建议共享一些数据,它也是一种方式。就我而言,我希望从目标对象中获取有关电子邮件主题和正文的详细信息。

围绕建议的代码:

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);
    }
}

请分享您对此方法的反馈/疑问。

答案 2 :(得分:0)

AFAIK没有切入点的表达......

您应该考虑配置记录器以执行您所需的操作。 将ae.printStackTrace();替换为类似logger.warn(ae)的内容(无论如何都要将堆栈跟踪打印到控制台是非常糟糕的做法),并配置日志工具的一个电子邮件发送追加器,例如: log4j'slogback's SMTPAppender。此外,为了简化配置,您可以使用业务专用记录器。

如果你真的想在这里使用方面,我认为你必须期望所有的业务例外至少冒出一个方法,以便使用afterThrowing建议。

答案 3 :(得分:0)

据我所知,为什么在业务逻辑代码中抛出了AuthenticationException。如果你真的想使用aop,那么请保持业务代码远离交叉问题。 在您的情况下,您可以从业务逻辑中获取AuthenticationException代码。在进入业务逻辑操作之前应用aop.for示例

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(

        }
    }
}

自定义例外

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
            }
        }
    }