Java 使用注释进行异常处理?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19389808/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 16:43:43  来源:igfitidea点击:

Using annotations for exception handling?

javaexceptionexception-handlingannotationstry-catch

提问by user1017413

Let's say I have a method that throws an Exception of some kind. The exception-throwing code lies in a third-party library that access an external service. I have a few classes that do a good deal of work with external services and there is a LOT of exception handling throughout to deal with potential problems. The issue I'm hitting is that I may have a lot of exceptions, but I may only need to perform one of a few actions if there is one, and there are a ton of try/catch blocks peppered about. The type of exception may not even be relevant, or different methods may throw the same type of exception, but different actions need to be taken depending on the method throwing it.

假设我有一个抛出某种异常的方法。异常抛出代码位于访问外部服务的第三方库中。我有几个类可以与外部服务进行大量工作,并且有很多异常处理贯穿始终以处理潜在问题。我遇到的问题是我可能有很多异常,但如果有的话,我可能只需要执行几个操作之一,并且有大量的 try/catch 块。异常的类型甚至可能不相关,或者不同的方法可能抛出相同类型的异常,但需要根据抛出它的方法采取不同的操作。

What I'm looking for is an annotation that can supplant try/catch and simply dictate the behavior to be taken when there is an exception in that method. I know that Spring ApsectJ can do this sort of thing, but I'm not currently able to easily add any new dependencies or modify the pom to adjust existing ones. As such, I'm hoping that this can be accomplished with a custom annotation. For example:

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

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

I would presume that a separate class would handle exceptions, of course. An additional difficulty is that I would need the ServiceObj that is passed as well. If makeThingsHappen() fails, I may need obj to perform additional actions. The action variable will tell the handler class what to do with obj.

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

Can this be done without severe muckery, or am I hoping for something that may not exist?

这可以在没有严重破坏的情况下完成吗,还是我希望可能不存在的东西?

采纳答案by user1017413

This should be a low-level process, and it doesn't mean we cannot have the same thing with current level, but it may needs a bunch of code and would complex the system a little. However my suggestion would be like this(I hope I got it correct), first define an interface for who wants to process exceptions, something like this.

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

interface ExceptionHandler{
  void handleException(Throwable t);
}

then provide an annotation for user(API) to mark its methods may throws some exception.

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

next we need a interface to start to call the method which may throws exception, something like this.

接下来我们需要一个接口来开始调用可能抛出异常的方法,就像这样。

interface Caller{
  void callMethod()throws Throwable;
}

then you need a guy who take care and manage the flow of the execution and call the possible exception handler

那么你需要一个负责管理执行流程并调用可能的异常处理程序的人

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

and finally, lets have some example, it works very well for me, it's cool. the exception handler.

最后,让我们举一些例子,它对我来说效果很好,很酷。异常处理程序。

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());
  }
}

and the method caller.

和方法调用者。

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

as you see, the user HAS TO call the methods by the callmethod()method, you would also omit the Callerinterface, and use annotation to declare more than one method in a class that it needs a bunch of extra codez. I hope I could give some hand.

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

回答by user1017413

Thanks for the help, all. I looked into Spring AOP, but ultimately decided against it. I wound up using try/catch blocks, but creating a handler that is injected into each class and wrapping any thrown exceptions in my own exception class, then passing that to the handler in a single line. It's a bit similar to the suggestion by user2511414 in that there's a dedicated handler, but I gave up on annotations. I have a good number of try/catch blocks, but at least I've kept the majority of the handling logic out. A quick rundown of my solution in case other folks find this, which is a bit obfuscated, but you can still get the point:

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

}

Then implement the IExceptionHandler with a concrete class that takes MyException, reads out the additional data, and performs actions based on it. Then each block that might throw such an exception can be caught like so:

然后使用接受 MyException 的具体类实现 IExceptionHandler,读出附加数据,并基于它执行操作。然后可以像这样捕获可能抛出此类异常的每个块:

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

Now most of the nitty-gritty is encapsulated in the handler and the try/catch blocks are minimal. The handler can log the stack trace of the original exception, maybe do other things based on it, and then perform your own actions based on the enum. Maybe it isn't the perfect solution, but it works well enough here.

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