2013-10-15 5 views
8

내가 예외를 던지는 메서드가 있다고 가정 해 보겠습니다. 예외 발생 코드는 외부 서비스에 액세스하는 타사 라이브러리에 있습니다. 외부 서비스에 대한 많은 작업을 수행하는 몇 가지 클래스가 있으며 잠재적 인 문제를 다루는 데는 예외가 많이 있습니다. 내가 부딪히는 문제는 많은 예외가있을 수 있지만 몇 가지 조치가있을 경우에만 수행해야 할 수도 있고 try/catch 블록이 너무 많아서 그럴 수 있습니다. 예외의 유형은 관련성이 없거나 다른 메소드가 동일한 유형의 예외를 던질 수 있지만이를 던지는 메소드에 따라 다른 조치를 취해야합니다.예외 처리에 주석을 사용 하시겠습니까?

내가 찾고있는 것은 try/catch를 대신 할 수있는 주석이며 해당 메소드에서 예외가있을 때 수행 할 동작을 지정하는 것입니다. 나는 Spring ApsectJ가 이런 종류의 일을 할 수 있다는 것을 알고 있지만, 나는 현재 새로운 의존성을 쉽게 추가하거나 기존의 것을 조정할 수 없다. 따라서, 나는 이것이 custom annotation으로 달성 될 수 있기를 바라고있다. 예를 들면 다음과 같습니다.

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

물론 예외를 처리하는 클래스가 있다고 가정합니다. 추가로 어려운 점은 전달 된 ServiceObj가 필요하다는 것입니다. makeThingsHappen()이 실패하면 추가 작업을 수행하기 위해 obj가 필요할 수 있습니다. 액션 변수는 핸들러 클래스에게 obj와 무엇을할지 알려줄 것입니다.

심각한 문제없이이 작업을 수행 할 수 있습니까, 아니면 존재하지 않을 수있는 것이 있습니까?

+0

주석은 독자적으로 동작을 추가하지 않습니다. 그것들은 메타 데이터입니다. 해당 동작을 발견하면 추가 할 수있는 파서를 제공해야합니다. –

+0

나는 그것을 사소한 muckery라고 부를 것이다. 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(); 
} 

다음 우리는 할 수있다 예외, 이런 식으로 뭔가를 throw하는 메서드를 호출하는 시작하는 인터페이스가 필요합니다.

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

지금 본질적인 측면의 대부분은 핸들러에 캡슐화되고 시도/catch 블록을 최소화합니다. 처리기는 원래 예외의 스택 추적을 기록 할 수 있으며,이 예외를 기반으로 다른 작업을 수행 한 다음 열거 형을 기반으로 사용자 고유의 작업을 수행 할 수 있습니다. 어쩌면 완벽한 해결책은 아니지만 여기서는 충분합니다.

관련 문제