2011-12-28 20 views
22

tengo una gran cantidad de código repetitivo que, básicamente, sigue este patrón:anotación de Java para envolver un método

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

Me encantaría crear mi propia anotación a limpiar mi código un poco:

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

Las firmas de método varían enormemente (dependiendo de la implementación real del método), pero la pieza de prueba/captura/finalmente es siempre la misma.

La anotación que tengo en mente se ajuste automáticamente el contenido del método anotado con todo el alboroto try...catch...finally.

He buscado alto y bajo de forma directa para hacer esto, pero no he encontrado nada. No sé, quizás no puedo ver el bosque de todos los árboles anotados.

Cualquier puntero sobre cómo podría implementar tal anotación sería muy apreciado.

+0

Me parece que una anotación no sería necesaria para esto; puedes pasar implementaciones que definen 'implementationOfDoSomething()' y (opcionalmente, quizás) 'genericCleanUpMethod()' como argumentos a 'doSomething()', llámalos dentro del 'try/catch/finally', y luego solo invocas' doSomething() 'siempre que necesites la lógica repetitiva? –

+1

Tu respuesta está en AOP, ¿has investigado esto? – smp7d

+0

Según tengo entendido, tiene este mismo try/catch/finally en múltiples métodos. Así que no hay solo un doSomething, sino más bien como doSomething1, doSomething2, ... todo con el mismo try/catch/finally que él quiere extraer de la anotación – jeff

Respuesta

19

Para hacer esto, necesitaría algún marco AOP que usaría un proxy alrededor de su método. Este proxy capturaría la excepción y ejecutaría el bloque finally. Francamente, si no usas un framework que soporte AOP ya, no estoy seguro de que use uno solo para guardar estas pocas líneas de código od.

podría utilizar el siguiente patrón de hacer esto de una manera más elegante, sin embargo:

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

simplemente utilicé Callable<Void> como una interfaz, pero se puede definir su propio Command interfaz:

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

y así evitar la necesidad de utilizar un genérico Callable<Void> y devolver nulo desde el Llamado.

EDITAR: en caso de que desee devolver algo de sus métodos, haga que el método logAndCleanup() sea genérico. Aquí está un ejemplo 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: Y con Java 8, el código envuelta puede ser un poco más bonita:

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

+1 - Esto es lo que estaba viendo en mis comentarios sobre la pregunta. –

+0

Esto es muy similar a mi propia solución "Plan B", en caso de que las anotaciones no sean prácticas. Lo cual parece ser el caso. ¡Muchas gracias! – TroutKing

+0

Gracias por esto, funciona muy bien si no devuelve nada, pero ¿qué pasa si 'invocable' necesita devolver algo. ¿Tiene que crear un 'ExecutorService' como se describe aquí ?: http://stackoverflow.com/a/5516955/293280 –

0

afaik tendrías que controlar cada llamada de método para la anotación @TryCatchWithLoggingAndCleanUp, lo que sería muy tedioso. Básicamente, usted puede obtener las anotaciones de cada método por reflexión y luego hacer su manejo y registro de excepciones. pero no estoy seguro de que quieras hacer eso.

3

puede implementar procesador de anotación y anotación de sí mismo y código del instrumento cada vez que cuando lo hace la compilación (javac -processor). Otra forma es usar AOP, por ejemplo, AspectJ o Spring AOP (si usa Spring).

11

Se podría usar proxies dinámicos para implementar esto. Se necesita un poco de configuración, pero una vez hecho, es bastante sencillo.

Primero, defina una interfaz y coloque la anotación en la interfaz.

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

Ahora, cuando se desea proporcionar una implementación de la interfaz a un consumidor, no proporcionan con él de la ejecución real, pero en lugar de un proxy a ella.

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

Luego, implemente 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 
} 
Cuestiones relacionadas