2013-10-15 31 views
8

Załóżmy, że mam metodę, która generuje wyjątek. Kod rzucający wyjątki znajduje się w bibliotece innej firmy, która uzyskuje dostęp do usługi zewnętrznej. Mam kilka zajęć, które wykonują dużo pracy z usługami zewnętrznymi i istnieje wiele przypadków obsługi wyjątków, aby poradzić sobie z potencjalnymi problemami. Kwestia, którą uderzam, polega na tym, że mogę mieć wiele wyjątków, ale mogę wykonać tylko jedną z kilku akcji, jeśli taka istnieje, i jest tam mnóstwo bloków try/catch. Typ wyjątku może nie być odpowiedni, lub różne metody mogą powodować ten sam typ wyjątku, ale należy podjąć różne działania w zależności od metody jego wyrzucania.Stosowanie adnotacji do obsługi wyjątków?

Czego szukam to adnotacja, która może zastąpić try/catch i po prostu dyktować zachowanie, które należy podjąć, gdy istnieje wyjątek w tej metodzie. Wiem, że Spring ApsectJ może robić tego rodzaju rzeczy, ale obecnie nie jestem w stanie łatwo dodać nowych zależności lub zmodyfikować pom, aby dostosować istniejące. W związku z tym mam nadzieję, że można to osiągnąć za pomocą niestandardowej adnotacji. Na przykład:

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

Zakładam, że osobna klasa poradzi sobie oczywiście z wyjątkami. Dodatkową trudnością jest to, że potrzebuję ServiceObj, który również został przekazany. Jeśli makeThingsHappen() nie powiedzie się, może potrzebować obiektu obj, aby wykonać dodatkowe akcje. Zmienna akcji powie klasie handler, co zrobić z obiektem.

Czy można to zrobić bez surowego mruknięcia, czy mam nadzieję na coś, co może nie istnieć?

+0

Adnotacje nie dodają żadnych zachowań na własną rękę. Są to metadane. Musisz udostępnić analizator składni, który może dodać to zachowanie, jeśli je znajdzie. –

+0

Nazwałbym to drobne muckery; może być obsługiwany trywialnie z AOP lub manipulacją bajtowym kodem. –

Odpowiedz

17

To powinien być proces niskiego poziomu i nie oznacza, że ​​nie możemy mieć tego samego z aktualnym poziomem, ale może potrzebować mnóstwa kodu i trochę skomplikuje system. Jednak moja sugestia byłaby podobna (mam nadzieję, że poprawne), najpierw zdefiniuj interfejs dla osób, które chcą przetwarzać wyjątki, coś takiego.

interface ExceptionHandler{ 
    void handleException(Throwable t); 
} 

następnie dodać adnotację dla użytkownika (API), aby zaznaczyć, że jego metody mogą powodować pewne wyjątki.

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

Następnie potrzebujemy interfejsu, aby rozpocząć wywoływanie metody, która może generować wyjątek, coś takiego.

interface Caller{ 
    void callMethod()throws Throwable; 
} 

to trzeba faceta, który dbać i zarządzania przepływem wykonania i wywołać możliwie obsługi wyjątku

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

i wreszcie, pozwala mieć jakiś przykład, to działa bardzo dobrze dla mnie, to jest fajne. Obsługa wyjątków.

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

i wywoływacza metody.

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

jak widać, użytkownik musi wywołać metody według metody callmethod(), należy również pominąć interfejs Caller i używać adnotacji zadeklarować więcej niż jedną metodę w klasie, że potrzebuje kilka dodatkowych codez. Mam nadzieję, że mógłbym podać trochę.

2

Dzięki za pomoc, wszystko. Spojrzałem na Spring AOP, ale ostatecznie zdecydowałem przeciwko niemu.Zlikwidowałem użycie bloków try/catch, ale utworzyłem program obsługi, który jest wstrzykiwany do każdej klasy i zawijanie wszelkich wyrzuconych wyjątków w mojej własnej klasie wyjątku, a następnie przekazywanie do obsługi w jednej linii. Jest trochę podobny do sugestii użytkownika 2511414, ponieważ istnieje dedykowany moduł obsługi, ale zrezygnowałem z adnotacji. Mam sporą liczbę bloków try/catch, ale przynajmniej zachowałem większość logiki obsługi. Szybki przegląd mojego rozwiązania w przypadku innych ludzi znaleźć ten, który jest nieco ukrywane, ale wciąż można dostać rzeczy:

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

} 

Następnie wdrożenia IExceptionHandler z betonu klasy, które odbywają MyException, odczytuje dodatkowe danych i wykonuje działania na ich podstawie. Następnie każdy blok, który może rzucić taki wyjątek może zostać złapany tak:

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

Teraz większość sedna jest zawarta w module obsługi i bloki try/catch są minimalne. Program obsługi może rejestrować ślad stosu oryginalnego wyjątku, może wykonywać na nim inne działania, a następnie wykonywać własne działania na podstawie wyliczenia. Może to nie jest idealne rozwiązanie, ale działa tutaj wystarczająco dobrze.

Powiązane problemy