Использование аннотаций для обработки исключений?

предположим, у меня есть метод, который выдает какое-то исключение. Код исключения находится в сторонней библиотеке, которая обращается к внешней службе. У меня есть несколько классов, которые много работают с внешними службами, и есть много обработки исключений для решения потенциальных проблем. Проблема в том, что у меня может быть много исключений, но мне может потребоваться выполнить только одно из нескольких действий, если есть один, и есть тонна блоков try/catch засыпали около. Тип исключения может даже не быть релевантным, или разные методы могут вызывать один и тот же тип исключения, но в зависимости от метода должны быть предприняты различные действия.

то, что я ищу, - это аннотация, которая может заменить try/catch и просто диктовать поведение, которое должно быть принято, когда в этом методе есть исключение. Я знаю, что Spring ApsectJ может делать такие вещи, но в настоящее время я не могу легко добавлять новые зависимости или изменять pom для настройки существующих. Таким образом, я надеюсь, что это может быть выполнено с помощью пользовательской аннотации. Например:

@Catcher(action=SomeEnum.SOME_ACTION)
public void doSomething(ServiceObj obj) throws SomeException {
    ExternalService.makeThingsHappen(obj);
}

Я бы предположил, что отдельный класс будет обрабатывать исключения, конечно. Дополнительная трудность заключается в том, что мне понадобится ServiceObj, который также передается. Если makeThingsHappen () терпит неудачу, мне может понадобиться obj для выполнения дополнительных действий. Переменная action сообщит классу обработчика, что делать с obj.

можно ли это сделать без серьезных muckery, или я надеялся на что-то, что может не существовать?

2 ответов


это должен быть процесс низкого уровня, и это не означает, что мы не можем иметь то же самое с текущим уровнем, но ему может потребоваться куча кода и немного усложнит систему. Однако мое предложение было бы таким (надеюсь, я правильно понял), сначала определите интерфейс для тех, кто хочет обрабатывать исключения, что-то вроде этого.

interface ExceptionHandler{
  void handleException(Throwable t);
}

затем укажите аннотацию для пользователя (API), чтобы отметить, что его методы могут вызывать некоторые исключения.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Catch{
  public Class<? extends ExceptionHandler> targetCatchHandler();
  public Class<? extends Throwable> targetException() default Exception.class;
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface CatchGroup{
  public Catch[] catchers();
}

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

interface Caller{
  void callMethod()throws Throwable;
}

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

class MethodCaller{
  /*
   * @param isntance: instance which implemented the Caller interface
   */
  public static void callMethod(Caller instance)
      throws Exception {
    Method m = instance.getClass().getMethod("callMethod");
    Annotation as[] = m.getAnnotations();
    Catch[] li = null;
    for (Annotation a : as) {
      if (a.annotationType().equals(CatchGroup.class)) {
        li = ((CatchGroup) a).catchers();
      }
      // for(Catch cx:li){cx.targetException().getName();}
    }
    try {
      instance.callMethod();
    } catch (Throwable e) {
      Class<?> ec = e.getClass();
      if (li == null) {
        return;
      }
      for (Catch cx : li) {
        if (cx.targetException().equals(ec)) {
          ExceptionHandler h = cx.targetCatchHandler().newInstance();
          h.handleException(e);
          break;
        }
      }
    }
  }
}

и, наконец, давайте приведем пример, он работает очень хорошо для меня, это круто. обработчик исключений.

public class Bar implements ExceptionHandler{//the class who handles the exception
  @Override
  public void handleException(Throwable t) {
    System.out.println("Ta Ta");
    System.out.println(t.getMessage());
  }
}

и вызывающий метод.

class Foo implements Caller{//the class who calls the method
  @Override
  @CatchGroup(catchers={ 
      @Catch(targetCatchHandler=Bar.class,targetException=ArithmeticException.class),
      @Catch(targetCatchHandler=Bar.class,targetException=NullPointerException.class)})
  public void callMethod()throws Throwable {
    int a=0,b=10;
    System.out.println(b/a);
  }
  public static void main(String[] args) throws Exception {
    Foo foo=new Foo();
    MethodCaller.callMethod(foo);
  }
}

как вы видите, пользователь должен вызвать методы с помощью callmethod() способ, вы также опустите Caller интерфейс, и использовать аннотации, чтобы объявить более одного метода в классе, который он должен кучу дополнительных codez. Надеюсь, я смогу помочь.


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

public enum DoThisEnum {
    DO_THIS,
    DO_THAT,
    DO_OTHER_THING;
}

public class MyException extends Exception {

    private DoThisEnum doThis;
    private MyObject dataObj;

    //Constructor, overloaded to run super(originalException) or super() 
    //as well as taking doThis and dataObj as parameters
    //Getters, setters, etc
}

public interface IExceptionHandler {

    void handleException(MyException exception);

}

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

...
try {
    doSomething(Object data);
} catch (SomeException e) {
    handler.handleException(new MyException(e, DoThisEnum.DO_THAT, data));
    //Anything else to do, maybe return or return null, rethrow, etc.
}

теперь большая часть nitty-gritty инкапсулируется в обработчик и блоки try / catch минимальны. Обработчик может регистрировать трассировку стека исходного исключения, возможно, делать другие вещи на его основе, а затем выполнять собственные действия на основе перечисления. Возможно, это не идеальное решение, но здесь оно работает достаточно хорошо.