Spring AOP – 从catch块调用建议

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

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

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

try{ //normal processing } catch (AuthenticationException ae) { ae.printStackTrace(); req.setAttribute("msg", ae.getMessage()); //execute advice at this point return loginPage; } 

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

最好的祝福

我知道你要避免使用完整的AspectJ而不是Spring AOP。 (顺便说一句,我想知道为什么很多人都如此害怕它。)无论如何,在AspectJ中,通过handler()切入点很容易拦截exception处理程序执行(= catch块)。 但有一个限制:它只适用于before()建议,而不适用after()around() 。 这是由于编译器的限制。 查看exception处理程序的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中的随机值抛出exception。 占所有案例的50%,而zot总是抛出除零exception。 因此输出将随着运行而不同。

那么,如果所有exception都被静默吞没而不记录,我们如何才能知道发生了什么? 像这样:

 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并阅读/更新一些属性等等。

好的,经过像Spring in Action这样的参考书之后,我开始知道我们无法在java代码中的任意点调用spring建议。 Spring in Action书籍建议您查看AspectJ,以便对切点进行细粒度控制。

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

1)对于只在发生exception时要调用建议的方法,请使用Around建议。 就像我的情况一样,我想在发生exception时发送电子邮件通知,然后我们离开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); } } 

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

AFAIK没有切入点表达式……

您应该考虑配置记录器以执行您所需的操作。 替换ae.printStackTrace(); 通过logger.warn(ae)类的东西(将logger.warn(ae)打印到控制台是非常糟糕的做法),并配置一个日志工具的电子邮件发送追加器,例如log4j或logback的 SMTPAppender。 此外,为了使您的配置更容易,您可以使用业务专用记录器。

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

据我所知,为什么在业务逻辑代码中抛出了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 } } }