2013-10-15 41 views
8

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

这可以做到没有严重muckery,或者我希望有可能不存在的东西?

+0

注释不会自行添加行为。他们是元数据。你需要为它们提供一个解析器,如果它发现它们就可以添加该行为。 –

+0

我会称之为轻微泥泞;它可以通过AOP或一些字节码操作来处理。 –

回答

17

这应该是一个低级别的过程,并不意味着我们不能和当前级别有相同的事情,但它可能需要一堆代码,并且会使系统复杂一点。 然而,我的建议会是这样的(我希望我知道它是正确的),首先为谁想要处理异常定义一个接口,就像这样。

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接口,并使用注解,因为它需要大量额外的一类声明的方法不止一种codez。 我希望我可以举手。

2

感谢您的帮助,所有。我看着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); 

} 

然后用一个具体的类,它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块是最小的。处理程序可以记录原始异常的堆栈跟踪,也可以基于它来做其他事情,然后根据枚举执行自己的操作。也许这不是一个完美的解决方案,但它在这里工作得很好。