使用批注进行异常处理?

假设我有一个抛出某种异常的方法。异常引发代码位于访问外部服务的第三方库中。我有几个类对外部服务做了大量的工作,并且在整个过程中有很多异常处理来处理潜在的问题。我遇到的问题是,我可能有很多例外,但我可能只需要执行几个操作中的一个(如果有的话),并且有大量的尝试/捕获块。异常的类型甚至可能不相关,或者不同的方法可能会引发相同类型的异常,但需要根据引发异常的方法采取不同的操作。

我正在寻找的是一个注释,它可以取代尝试/捕获,并简单地指示当该方法中存在异常时要采取的行为。我知道Spring ApsectJ可以做这种事情,但我目前无法轻松添加任何新的依赖项或修改pom以调整现有依赖项。因此,我希望这可以通过自定义注释来完成。例如:

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

当然,我会假设一个单独的类来处理异常。另一个困难是,我也需要通过的ServiceObj。如果 makeThingsHappen() 失败,我可能需要 obj 来执行其他操作。操作变量将告诉处理程序类如何处理 obj。

这可以在没有严重混乱的情况下完成,还是我希望有一些可能不存在的东西?


答案 1

这应该是一个低级过程,这并不意味着我们不能与当前关卡有同样的东西,但它可能需要一堆代码,并且会使系统变得复杂一些。但是,我的建议是这样的(我希望我做对了),首先为想要处理异常的人定义一个接口,就像这样。

interface ExceptionHandler{
  void handleException(Throwable t);
}

然后为 user(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);
  }
}

如您所见,用户必须通过该方法调用方法,您还会省略接口,并使用注释在一个类中声明多个方法,它需要一堆额外的codez。我希望我能伸出援手。callmethod()Caller


答案 2

谢谢你的帮助,大家。我研究了Spring AOP,但最终决定反对它。我最终使用 try/catch 块,但创建一个注入到每个类中的处理程序,并将任何抛出的异常包装在我自己的异常类中,然后在一行中将其传递给处理程序。这有点类似于user2511414的建议,因为有一个专用的处理程序,但我放弃了注释。我有大量的尝试/捕获块,但至少我保留了大部分处理逻辑。我的解决方案的快速概述,以防其他人发现这一点,这有点混淆,但你仍然可以得到这一点:

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

现在,大部分细节都封装在处理程序中,并且 try/catch 块是最小的。处理程序可以记录原始异常的堆栈跟踪,可能基于它执行其他操作,然后根据枚举执行自己的操作。也许它不是完美的解决方案,但它在这里工作得很好。


推荐