2011-12-28 22 views
22

Mam dużo kodu boilerplate że w zasadzie zachowuje ten wzór:Java adnotacji do owijania metody

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

Chciałbym stworzyć własną adnotacji oczyścić mój kod się trochę:

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

Sygnatury metod różnią się znacznie (w zależności od rzeczywistej implementacji metody), ale część wypalania/catch/finally jest zawsze taka sama.

Adnotacja, którą mam na myśli, automatycznie zawinąłaby zawartość opisanej metody z całym hoopla try...catch...finally.

Przeszukałem wysokie i niskie w prosty sposób, ale nic nie znalazłem. Nie wiem, może po prostu nie widzę lasu dla wszystkich opisywanych drzew.

Wszelkie wskazówki dotyczące tego, w jaki sposób mogę wprowadzić taką adnotację, byłyby mile widziane.

+0

Wydaje mi się, że adnotacja nie byłaby konieczna; możesz po prostu przekazać implementacje, które definiują 'implementationOfDoSomething()' i (opcjonalnie, być może) 'genericCleanUpMethod()' jako argumenty funkcji 'doSomething()', wywołaj je wewnątrz 'try/catch/finally', a następnie po prostu wywołaj' doSomething() 'kiedy potrzebujesz logiki standardowej? –

+1

Twoja odpowiedź leży w AOP, czy to zbadałeś? – smp7d

+0

Moje zrozumienie polega na tym, że ma tę samą metodę/catch/finally na wielu metodach. Tak więc nie ma tu tylko jednego zadania, ale bardziej przypominającego DoSomething1, DoSomething2, ... wszystko z tą samą próbą/catch/wreszcie, że chce wyodrębnić do adnotacji – jeff

Odpowiedz

19

Aby to zrobić, potrzebowałbyś pewnej struktury AOP, która używała proxy wokół twojej metody. Ten serwer proxy przechwyciłby wyjątek i wykonałby blok finally. Szczerze mówiąc, jeśli nie korzystasz już z frameworka obsługującego AOP, nie jestem pewien, czy użyłbym jednego, aby zapisać te kilka linii kodu.

Można użyć następującego wzoru to zrobić w bardziej elegancki sposób, ale:

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

Używałem Callable<Void> jako interfejs, ale można zdefiniować własne Command interfejs:

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

, a tym samym unikaj konieczności używania standardowego Callable<Void> i zwróć wartość null z opcji Callable.

EDYCJA: w przypadku, gdy chcesz zwrócić coś ze swoich metod, zmień metodę logAndCleanup(). Oto pełna przykład:

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: I z Java 8, owinięty kod może być nieco ładniejsza:

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

+1 - Właśnie o to pytałem w moich uwagach do pytania. –

+0

Jest to bardzo podobne do mojego własnego rozwiązania "Planu B", gdyby adnotacje okazały się niepraktyczne. Wydaje się, że tak jest. Dziękuję bardzo! – TroutKing

+0

Dziękuję za to, działa świetnie, jeśli nic nie zwracasz, ale co, jeśli 'wywoływany' musi coś zwrócić. Czy musisz utworzyć "ExecutorService" jak tutaj ?: http://stackoverflow.com/a/5516955/293280 –

0

afaik musisz monitorować każde wywołanie metody dla adnotacji @TryCatchWithLoggingAndCleanUp, co byłoby bardzo uciążliwe. zasadniczo można uzyskać adnotacje każdej metody przez odbicie, a następnie wykonać obsługę wyjątków i rejestrację. ale nie jestem pewien, czy chciałbyś to zrobić.

3

możesz zaimplementować procesor opisów i adnotacji sam i kod instrumentu za każdym razem, gdy wykonujesz kompilację (javac -processor). Innym sposobem jest użycie AOP, powiedzmy AspectJ lub Spring AOP (jeśli używasz Spring).

11

Można używać dynamicznych proxy do wdrożenia tego. Zajmuje trochę konfiguracji, ale po wykonaniu jest dość proste.

Najpierw definiujesz interfejs i umieszczasz adnotację w interfejsie.

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

Teraz, gdy chcemy zapewnić implementację interfejsu do konsumenta, nie zapewniają mu z rzeczywistą realizacją, ale zamiast Pełnomocnictwo do niego.

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

Następnie zaimplementuj 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 
} 
Powiązane problemy