Spring AOP - Вызов совета из блока catch

Цель: отправлять электронное письмо администратору каждый раз, когда возникает исключение при выполнении бизнес-логики.

До сих пор я сталкивался с «подсказкой по метанию», которая работает, когда из целевого метода возникает исключение.

Это могло сработать для меня, но мне нужно выполнить дополнительную обработку с точки зрения настройки атрибута запроса и следующей страницы. Я не думаю, что было бы хорошей идеей поделиться объектами из целевого класса с советами, сделав эти объекты статическими. Сценарий кода показан ниже:

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

   //execute advice at this point

   return loginPage;
}

Пожалуйста. увидеть точку, в которой я хочу выполнить совет, и предложить соответствующее решение.

Наилучшие пожелания


person Piyush-Ask Any Difference    schedule 18.03.2013    source источник
comment
Уточните пожалуйста свой вопрос. Откуда req и почему / где вы должны делать что-нибудь статичное? Пожалуйста, предоставьте минимальный образец кода с одним или двумя классами и кодом вашего аспекта, воспроизводящим вашу проблему, чтобы мы могли ее понять. В противном случае я понятия не имею, как тебе помочь.   -  person kriegaex    schedule 18.03.2013
comment
Разве для этого нельзя просто использовать javax.servlet.Filter?   -  person Potejciak    schedule 18.03.2013
comment
@Potejciak да, определенно, я могу использовать другой механизм, чтобы отделить этот материал, отправляя уведомление по электронной почте, когда исключение возникает несколькими способами, включая ваш. Но заказчик попросил для этого поддержку Spring AOP :(. В настоящее время это реализовано только с использованием вашего подхода.   -  person Piyush-Ask Any Difference    schedule 18.03.2013
comment
Я знаю, что это старый, но до сих пор числится без ответа. Не могли бы вы принять и проголосовать за мой ответ, если он кажется уместным? Спасибо.   -  person kriegaex    schedule 09.06.2014


Ответы (4)


Я знаю, что вы хотите избежать полного AspectJ в отличие от Spring AOP. (Кстати, мне интересно, почему многие люди так этого боятся.) В любом случае, в AspectJ действительно легко перехватить выполнение обработчика исключений (= блоки catch) с помощью handler() pointcut. Однако есть одно ограничение: он работает только с 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";
    }
}

Как видите, методы foo и bar генерируют исключения на основе случайных значений в ок. 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 и чтение / обновление некоторых свойств и т. д.

person kriegaex    schedule 06.04.2013

Хорошо, после просмотра справочников, таких как Spring in Action, я узнал, что нет способа, с помощью которого мы можем вызывать советы Spring в произвольных точках нашего java-кода. Книга Spring in Action рекомендует взглянуть на AspectJ для контроля мелкой зернистости при точечной резке.

Чтобы избежать добавления AspectJ, я наткнулся на следующее решение, которое могло помочь другим и сэкономить их драгоценное время:

1) Используйте Around advice для метода, в котором вы хотите вызывать совет только при возникновении исключения. Как и в моем случае, я хочу отправить уведомление по электронной почте, когда возникает исключение и мы выходим из блока 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);
    }
}

Поделитесь своими отзывами / вопросами по этому подходу.

person Piyush-Ask Any Difference    schedule 19.03.2013

AFAIK для этого нет выражения pointcut ...

Вам следует подумать о настройке вашего регистратора на то, что вам нужно. Замените ae.printStackTrace(); чем-то вроде logger.warn(ae) (выводить трассировки стека на консоль в любом случае - плохая практика) и настройте одно из приложений для отправки электронной почты вашего инструмента ведения журнала, например log4j's или logback's SMTPAppender. Кроме того, чтобы упростить настройку, вы можете использовать специальный регистратор для бизнеса.

Если вы действительно хотите использовать здесь аспекты, я думаю, вам придется ожидать, что все ваши бизнес-исключения поднимут хотя бы один метод, чтобы использовать afterThrowing советы.

person Vincent    schedule 18.03.2013

Насколько я понимаю, почему AuthenticationException выбрасывается в коде бизнес-логики. Если вы действительно хотите использовать aop, держите бизнес-код URS подальше от сквозных проблем. В вашем случае вы можете извлечь код AuthenticationException из бизнес-логики. Перед входом в операцию бизнес-логики примените aop. Например

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
            }
        }
    }
person abishkar bhattarai    schedule 06.04.2013