【问题标题】:Spring AOP - Invoking advice from catch blockSpring AOP - 从 catch 块调用建议
【发布时间】:2013-03-18 09:46:59
【问题描述】:

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

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

这对我来说可能效果很好,但我必须在设置请求属性和下一页方面做一些额外的处理。我不认为通过使这些对象静态来共享来自目标类的对象和建议不是一个好主意。代码场景如下图:

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

   //execute advice at this point

   return loginPage;
}

请。查看我想要执行建议的点并相应地提出解决方案。

最好的问候

【问题讨论】:

  • 请澄清您的问题。 req 来自哪里以及为什么/在哪里应该使任何东西成为静态的?请提供一个包含一个或两个类的最小代码示例和您的方面代码,重现您的问题,以便我们理解它。否则我不知道如何帮助你。
  • 你不能只使用 javax.servlet.Filter 吗?
  • @Potejciak 是的,当包括您的多种方式发生异常时,我可以使用其他机制来分离发送电子邮件通知的这些内容。但是客户已经要求 Spring AOP 支持这个:(。目前它只使用你的方法实现。
  • 我知道这是旧的,但仍列为未答复。如果它看起来合适,请您接受并支持我的回答吗?谢谢。

标签: java spring aop spring-aop


【解决方案1】:

我知道您希望避免使用完整的 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 并读取/更新一些属性等等。

【讨论】:

    【解决方案2】:

    好的,在阅读了 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);
        }
    }
    

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

    【讨论】:

      【解决方案3】:

      AFAIK 这个没有切入点表达式...

      您应该考虑配置您的记录器来满足您的需要。 将ae.printStackTrace(); 替换为logger.warn(ae) 之类的东西(无论如何,将堆栈跟踪打印到控制台是非常糟糕的做法),并配置日志工具的电子邮件发送附加程序之一,例如log4j'slogback's SMTPAppender。此外,为了使您的配置更容易,您可以使用业务专用的记录器。

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

      【讨论】:

        【解决方案4】:

        据我了解,为什么在业务逻辑代码中会抛出 AuthenticationException。如果您真的想使用 aop,请让您的业务代码远离横切关注点。 在您的情况下,您可以从业务逻辑中取出AuthenticationException代码。在进入业务逻辑操作之前应用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(
        
                }
            }
        }
        

        自定义异常

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多