Using annotations for exception handling?

前端 未结 2 1207
滥情空心
滥情空心 2020-12-05 08:07

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 cla

相关标签:
2条回答
  • 2020-12-05 08:30

    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.

    @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 Caller interface, 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.

    0 讨论(0)
  • 2020-12-05 08:33

    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:

    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:

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

    0 讨论(0)
提交回复
热议问题