methods how - Annotazione Java per il wrapping di un metodo




value code (5)

Ho un sacco di codice boilerplate che fondamentalmente segue questo modello:

function doSomething() {
  try {
    [implementation]
    [implementation]
    [implementation]
    [implementation]
  } catch (Exception e) {
    MyEnv.getLogger().log(e);
  } finally {
    genericCleanUpMethod();
  }
}

Mi piacerebbe creare la mia annotazione per pulire un po 'il mio codice:

@TryCatchWithLoggingAndCleanUp
function doSomething() {
  [implementation]
  [implementation]
  [implementation]
  [implementation]
}

Le firme del metodo variano in modo selvaggio (a seconda dell'implementazione effettiva del metodo), ma il boilerplate try / catch / finally part è sempre lo stesso.

L'annotazione che ho in mente avvolgerebbe automaticamente il contenuto del metodo annotato con l'intero try...catch...finally hoopla.

Ho cercato in alto e in basso per un modo semplice per farlo, ma non ho trovato nulla. Non so, forse non riesco a vedere i boschi per tutti gli alberi annotati.

Qualsiasi suggerimento su come implementare questa annotazione sarebbe molto apprezzato.


Answers

Per fare ciò, è necessario un framework AOP che utilizzi un proxy attorno al proprio metodo. Questo proxy catturerebbe l'eccezione ed eseguirà il blocco finale. Francamente, se non usi già un framework che supporti AOP, non sono sicuro che ne userei uno solo per salvare queste poche righe di codice.

Puoi usare il seguente schema per farlo in un modo più elegante, però:

public void doSomething() {
    logAndCleanup(new Callable<Void>() {
        public Void call() throws Exception {
            implementationOfDoSomething();
            return null;
        }
    });
}

private void logAndCleanup(Callable<Void> callable) {
    try {
        callable.call();
    } 
    catch (Exception e) {
        MyEnv.getLogger().log(e);
    } 
    finally {
        genericCleanUpMethod();
    }
}

Ho appena usato Callable<Void> come interfaccia, ma potresti definire la tua interfaccia di Command :

public interface Command {
    public void execute() throws Exception;
}

e quindi evitare la necessità di usare un Callable<Void> generico Callable<Void> e restituire null dal Callable.

EDIT: nel caso in cui si desideri restituire qualcosa dai propri metodi, quindi rendere generico il metodo logAndCleanup() . Ecco un esempio completo:

public class ExceptionHandling {
    public String doSomething(final boolean throwException) {
        return logAndCleanup(new Callable<String>() {
            public String call() throws Exception {
                if (throwException) {
                    throw new Exception("you asked for it");
                }
                return "hello";
            }
        });
    }

    public Integer doSomethingElse() {
        return logAndCleanup(new Callable<Integer>() {
            public Integer call() throws Exception {
                return 42;
            }
        });
    }

    private <T> T logAndCleanup(Callable<T> callable) {
        try {
            return callable.call();
        }
        catch (Exception e) {
            System.out.println("An exception has been thrown: " + e);
            throw new RuntimeException(e); // or return null, or whatever you want
        }
        finally {
            System.out.println("doing some cleanup...");
        }
    }

    public static void main(String[] args) {
        ExceptionHandling eh = new ExceptionHandling();

        System.out.println(eh.doSomething(false));
        System.out.println(eh.doSomethingElse());
        System.out.println(eh.doSomething(true));
    }
}

EDIT: E con Java 8, il codice spostato può essere un po 'più carino:

public String doSomething(final boolean throwException) {
    return logAndCleanup(() -> {                
        if (throwException) {
            throw new Exception("you asked for it");
        }
        return "hello";                
    });
}

Afaik dovresti monitorare ogni chiamata al metodo per l'annotazione @TryCatchWithLoggingAndCleanUp , che sarebbe molto noiosa. in pratica è possibile ottenere le annotazioni di ciascun metodo mediante riflessione e quindi eseguire la gestione e la registrazione delle eccezioni. ma non sono sicuro che vorresti farlo.


È possibile utilizzare i proxy dinamici per implementare questo. Ci vuole un po 'di configurazione, ma una volta fatto, è piuttosto semplice.

Innanzitutto, definisci un'interfaccia e posiziona l'annotazione sull'interfaccia.

public interface MyInterface {
    @TryCatchWithLogging
    public void doSomething();
}

Ora, quando si desidera fornire un'implementazione dell'interfaccia a un consumatore, non fornire con lui l'effettiva implementazione, ma piuttosto un Proxy ad esso.

MyInterface impl = new java.lang.reflect.Proxy.newProxyInstance(
                         Impl.class.getClassLoader(), 
                         Impl.class.getInterfaces(), YourProxy(new Impl());

Quindi implementa YourProxy.

public class YourProxy implements InvocationHandler {
....

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         if ( method.isAnnotationPresent(TryCatchLogging.class) ) {
              // Enclose with try catch
}

è possibile implementare autonomamente annotazioni e processori di annotazione e codice strumento ogni volta che si esegue la compilazione ( javac -processor ). L'altro modo è usare AOP, ad esempio AspectJ o Spring AOP (se usi Spring).


Privato

  • Metodi, Variabili e Costruttori

Metodi, variabili e costruttori dichiarati privati ​​sono accessibili solo all'interno della classe dichiarata.

  • Classe e interfaccia

Il modificatore di accesso privato è il livello di accesso più restrittivo. La classe e le interfacce non possono essere private.

Nota

Le variabili dichiarate private sono accessibili al di fuori della classe se nella classe sono presenti metodi getter pubblici. Variabili, metodi e costruttori che sono dichiarati protetti in una superclasse sono accessibili solo dalle sottoclassi di un altro pacchetto o di qualsiasi classe all'interno del pacchetto della classe dei membri protetti.

protetta

  • Classe e interfaccia

Il modificatore di accesso protetto non può essere applicato alla classe e alle interfacce.

Metodi, i campi possono essere dichiarati protetti, tuttavia i metodi e i campi in un'interfaccia non possono essere dichiarati protetti.

Nota

L'accesso protetto consente alla sottoclasse di utilizzare il metodo o la variabile helper, impedendo al contempo a una classe non correlata di tentare di utilizzarla.

Pubblico

Una classe, un metodo, un costruttore, un'interfaccia, ecc., Dichiarato pubblico, è accessibile da qualsiasi altra classe.

Pertanto è possibile accedere a campi, metodi, blocchi dichiarati all'interno di una classe pubblica da qualsiasi classe appartenente all'universo Java.

  • Pacchetti diversi

Tuttavia, se la classe pubblica a cui stiamo tentando di accedere si trova in un pacchetto diverso, la classe pubblica deve ancora essere importata.

A causa dell'ereditarietà delle classi, tutti i metodi pubblici e le variabili di una classe sono ereditati dalle sue sottoclassi.

Predefinito -Nessuna parola chiave:

Il modificatore di accesso predefinito significa che non dichiariamo esplicitamente un modificatore di accesso per una classe, un campo, un metodo, ecc.

  • All'interno degli stessi pacchetti

Una variabile o un metodo dichiarato senza alcun modificatore del controllo di accesso è disponibile per qualsiasi altra classe nello stesso pacchetto. I campi di un'interfaccia sono implicitamente statici finali pubblici e i metodi in un'interfaccia sono di pubblico dominio.

Nota

Non possiamo sovrascrivere i campi Statici. Se provi a sovrascriverli, non mostra alcun errore, ma non funziona a parte noi.

Risposte correlate

Link di riferimenti

http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html http://www.tutorialspoint.com/java/java_access_modifiers.htm





java methods annotations