【问题标题】:Using annotations for exception handling?使用注释进行异常处理?
【发布时间】:2013-10-23 18:20:29
【问题描述】:

假设我有一个抛出某种异常的方法。抛出异常的代码位于访问外部服务的第三方库中。我有几个类与外部服务一起做了大量的工作,并且整个过程中有很多异常处理来处理潜在的问题。我遇到的问题是我可能有很多例外,但如果有一个,我可能只需要执行几个操作中的一个,并且有大量的 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。

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

【问题讨论】:

  • 注解不会自行添加行为。它们是元数据。您需要为它们提供一个解析器,如果它找到它们就可以添加该行为。
  • 我称之为小玩意;它可以通过 AOP 或一些字节码操作来轻松处理。

标签: java exception exception-handling annotations try-catch


【解决方案1】:

谢谢大家的帮助。我研究了 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.
}

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

【讨论】:

    【解决方案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接口,并使用注解在一个类中声明多个方法,它需要一堆额外的编解码器。 我希望我能伸出援手。

    【讨论】:

      猜你喜欢
      • 2013-05-27
      • 2012-08-05
      • 2017-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-29
      • 1970-01-01
      相关资源
      最近更新 更多