2013-10-15 15 views
8

Diciamo che ho un metodo che genera un'eccezione di qualche tipo. Il codice di lancio delle eccezioni si trova in una libreria di terze parti che accede a un servizio esterno. Ho alcune classi che fanno un buon lavoro con i servizi esterni e c'è un sacco di gestione delle eccezioni in tutto per far fronte a potenziali problemi. Il problema che sto colpendo è che potrei avere molte eccezioni, ma potrei aver bisogno solo di eseguire una delle poche azioni se ce n'è una, e ci sono un sacco di blocchi try/catch disseminati. Il tipo di eccezione potrebbe non essere nemmeno pertinente, o metodi diversi possono lanciare lo stesso tipo di eccezione, ma è necessario intraprendere azioni diverse a seconda del metodo che la lancia.Utilizzo delle annotazioni per la gestione delle eccezioni?

Quello che sto cercando è un'annotazione che può soppiantare try/catch e dettare semplicemente il comportamento da adottare quando c'è un'eccezione in quel metodo. So che Spring ApsectJ può fare questo genere di cose, ma al momento non sono in grado di aggiungere facilmente nuove dipendenze o modificare il pom per regolare quelle esistenti. Come tale, spero che questo possa essere realizzato con un'annotazione personalizzata. Ad esempio:

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

Suppongo che una classe separata gestisca le eccezioni, ovviamente. Un'altra difficoltà è che avrei bisogno anche del ServiceObj. Se makeThingsHappen() fallisce, potrei aver bisogno di obj per eseguire ulteriori azioni. La variabile action dirà alla classe del gestore cosa fare con obj.

Questo può essere fatto senza grave muckery, o sto sperando in qualcosa che potrebbe non esistere?

+0

Le annotazioni non aggiungono un comportamento autonomo. Sono metadati. È necessario fornire un parser per loro che possa aggiungere quel comportamento se li trova. –

+0

Lo definirei un piccolo muckery; può essere gestito banalmente con AOP o manipolazione di codice byte. –

risposta

17

Questo dovrebbe essere un processo di basso livello, e non significa che non possiamo avere la stessa cosa con il livello attuale, ma potrebbe richiedere un po 'di codice e complicherebbe un po' il sistema. Tuttavia il mio suggerimento sarebbe come questo (spero di averlo capito correttamente), prima definire un'interfaccia per chi vuole elaborare le eccezioni, qualcosa del genere.

quindi fornire un'annotazione per utente (API) per contrassegnare i suoi metodi può generare qualche eccezione.

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

successivo abbiamo bisogno di un'interfaccia per iniziare a chiamare il metodo che può generare eccezioni, qualcosa di simile.

interface Caller{ 
    void callMethod()throws Throwable; 
} 

allora avete bisogno di un ragazzo che si prendono cura e gestire il flusso di esecuzione e chiamare la possibile gestore di eccezioni

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

e, infine, consente di avere qualche esempio, funziona molto bene per me, è bello il gestore di eccezioni.

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

e il chiamante del metodo.

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

Come si vede, l'utente deve chiamare i metodi con il metodo callmethod(), si sarebbe anche omettere l'interfaccia Caller, e utilizzare l'annotazione di dichiarare più di un metodo in una classe che ha bisogno di un po 'di più codez. Spero di poter dare una mano.

2

Grazie per l'aiuto, tutto. Ho esaminato Spring AOP, ma alla fine ho deciso di non farlo.Ho finito usando i blocchi try/catch, ma creando un gestore che è stato iniettato in ogni classe e avvolgendo eventuali eccezioni generate nella mia stessa classe di eccezioni, passando poi al gestore in una singola riga. È un po 'simile al suggerimento di user2511414 in quanto c'è un gestore dedicato, ma ho rinunciato alle annotazioni. Ho un buon numero di blocchi try/catch, ma almeno ho tenuto fuori la maggior parte della logica di gestione. Un breve riassunto della mia soluzione nel caso in cui altre persone trovano questo, che è un po 'offuscato, ma è ancora possibile ottenere il punto:

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

} 

quindi implementare l'IExceptionHandler con una classe concreta che prende MyException, legge l'ulteriore dati ed esegue azioni basate su di esso. Poi ogni blocco che potrebbe lanciare una tale eccezione può essere intercettata in questo modo:

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

Ora la maggior parte del nocciolo è incapsulato nel gestore ed i blocchi try/catch sono minimi. Il gestore può registrare la traccia dello stack dell'eccezione originale, magari fare altre cose basate su di esso, quindi eseguire le proprie azioni basate sull'enumerazione. Forse non è la soluzione perfetta, ma funziona abbastanza bene qui.