web-dev-qa-db-ja.com

例外処理に注釈を使用していますか?

ある種の例外をスローするメソッドがあるとしましょう。例外をスローするコードは、外部サービスにアクセスするサードパーティのライブラリにあります。私には外部サービスで多くの作業を行うクラスがいくつかあり、潜在的な問題に対処するために全体を通して多くの例外処理があります。私が直面している問題は、多くの例外があるかもしれないということですが、いくつかのアクションがある場合にのみ実行する必要があり、たくさんの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が必要になる場合があります。アクション変数は、ハンドラークラスにobjの処理方法を指示します。

これはひどいマックなしで行うことができますか、それとも私は存在しないかもしれない何かを望んでいますか?

15
user1017413

これは低レベルのプロセスである必要があり、現在のレベルで同じことができないという意味ではありませんが、大量のコードが必要になる可能性があり、システムが少し複雑になります。しかし、私の提案はこのようになります(私はそれが正しいことを願っています)、最初に例外を処理したい人のためのインターフェースを定義します、このようなもの。

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

ご覧のとおり、ユーザーはcallmethod()メソッドでメソッドを呼び出す必要があります。また、Callerインターフェイスを省略し、アノテーションを使用して、必要なクラス内の複数のメソッドを宣言します。余分なcodezの束。手を差し伸べることができればと思います。

26
user2511414

助けてくれてありがとう、みんな。 Spring AOPを調べましたが、最終的には反対しました。最終的にはtry/catchブロックを使用しましたが、各クラスに挿入されるハンドラーを作成し、スローされた例外を自分の例外クラスでラップしてから、それを1行でハンドラーに渡しました。専用のハンドラーがあるという点で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);

}

次に、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.
}

これで、ほとんどの要点がハンドラーにカプセル化され、try/catchブロックが最小限に抑えられます。ハンドラーは、元の例外のスタックトレースをログに記録し、それに基づいて他のことを実行してから、列挙型に基づいて独自のアクションを実行できます。完璧な解決策ではないかもしれませんが、ここでは十分に機能します。

2
user1017413