2016-01-05 21 views
5

Desidero utilizzare AOP per intercettare tutte le eccezioni di runtime generate nel livello di servizio e rethrow come eccezioni di dominio.@AfterThrowing non funziona come previsto

@Aspect 
@Component 
public class ExceptionWrapperInterceptor { 

    @Pointcut("within(*.service.*)") 
    public void onlyServiceClasses() {} 

    @AfterThrowing(pointcut = "onlyServiceClasses()", throwing = "ex") 
    public void intercept(DataAccessException ex) throws Exception { 
     //throw DatabaseException 
    } 

    @AfterThrowing(pointcut = "onlyServiceClasses()", throwing = "ex") 
    public void intercept(RuntimeException ex) throws Exception { 
     //throw ServiceException 
    } 

} 

Il problema qui è che, con una sottoclasse di DataAccessException, il runtime esegue il metodo sbagliato. C'è una soluzione elegante a questo?

Primavera Versione: 4.2.4.RELEASE

P.S. Un unico metodo generico (leggere da altre questioni), con un sacco di instanceof non è elegante per me ;-)

Grazie Francesco

+0

Con "metodo sbagliato" si intende, che entrambi i metodi vengono eseguiti, giusto? – Betlista

+0

No. Solo il metodo 'intercetta (RuntimeException)'. – Francesco

+0

Puoi verificare la mia risposta e condividere ciò che è diverso nel tuo caso? Non hai condiviso la versione di Spring, la definizione di 'onlyServiceClasses' e altri dettagli ... – Betlista

risposta

3

credo, che la vostra aspettativa è sbagliato (che solo un metodo di intercettazione corrisponderà allo stesso modo del sovraccarico del metodo).

Ma mentre RuntimeException è madre di DataAccessException vengono eseguiti entrambi i metodi ...

spring.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> 

    <context:component-scan base-package="test" /> 

    <aop:aspectj-autoproxy /> 

</beans> 

AopTest

package test; 

import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 

public class AopTest { 

    public static void main(String[] args) { 
     ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring.xml"); 
     MyService ms = ac.getBean(MyService.class); 
     try { 
      ms.throw1(); 
     } catch (Exception e) { 
//   e.printStackTrace(); 
     } 
     try { 
      ms.throw2(); 
     } catch (Exception e) { 
//   e.printStackTrace(); 
     } 
    } 
} 

MyAspect

package test; 

import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class MyAspect { 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(DataAccessException ex) throws Exception { 
     //throw DatabaseException 
     System.out.println("DAE"); 
    } 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(RuntimeException ex) throws Exception { 
     //throw ServiceException 
     System.out.println("RE - " + ex.getClass()); 
    } 

} 

MyService

package test; 

import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Service; 

@Service 
public class MyService { 

    public void throw1() throws DataAccessException { 
     throw new MyDataAccessException("test"); 
    } 

    public void throw2() { 
     throw new NullPointerException(); 
    } 

    static class MyDataAccessException extends DataAccessException { 

     public MyDataAccessException(String msg) { 
      super(msg); 
     } 

    } 
} 

e nel registro si trova:

DAE 
RE - class test.MyService$MyDataAccessException 
RE - class java.lang.NullPointerException 

Maven dipendenze:

<dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context</artifactId> 
     <version>4.2.4.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-aspects</artifactId> 
     <version>4.2.4.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-tx</artifactId> 
     <version>4.2.4.RELEASE</version> 
    </dependency> 
.210

From Spring documentation:

Quando due pezzi di consulenza definita nel medesimo aspetto sia necessario eseguire nello stesso unirsi punto, l'ordinamento non è definita (dal momento che non v'è alcun modo per recuperare l'ordine di dichiarazione attraverso la riflessione per javac- classi compilate). Considera la possibilità di smantellare tali metodi di consulenza in un unico metodo di consulenza per punto di unione in ciascuna classe di aspetto o di rifattorizzare i consigli in classi di aspetto separate, che possono essere ordinate a livello di aspetto.

Quando ho provato a seguito della modifica del MyAspect:

package test; 

import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class MyAspect { 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(DataAccessException ex) throws Exception { 
     //throw DatabaseException 
     System.out.println("DAE"); 
     throw new IllegalArgumentException("DAE"); // added 
    } 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(RuntimeException ex) throws Exception { 
     //throw ServiceException 
     System.out.println("RE - " + ex.getClass()); 
     throw new IllegalArgumentException("RE"); // added 
    } 

} 

registro cambiato in:

DAE 
RE - class java.lang.IllegalArgumentException 
RE - class java.lang.NullPointerException 

e quando modificato per Exception ho ottenuto:

package test; 

import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class MyAspect { 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(DataAccessException ex) throws Exception { 
     //throw DatabaseException 
     System.out.println("DAE"); 
     throw new Exception("DAE2"); // changed 
    } 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(RuntimeException ex) throws Exception { 
     //throw ServiceException 
     System.out.println("RE - " + ex.getClass()); 
     throw new Exception("RE2"); // changed 
    } 

} 

il registro è stato

DAE 
RE - class java.lang.NullPointerException 

credo, che la soluzione al vostro "problema" è quello di avere due aspetti invece di uno e definire l'ordinamento:

package test; 

import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.core.Ordered; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class DaeAspect implements Ordered { 

    public int getOrder() { 
     return 200; 
    } 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(DataAccessException ex) throws Exception { 
     //throw DatabaseException 
     System.out.println("DAE"); 
     throw new IllegalAccessException("DAE2"); // based on my testing, this stops second aspect to apply 
    } 

} 

e

package test; 

import org.aspectj.lang.annotation.AfterThrowing; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.core.Ordered; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public class ReAspect implements Ordered { 

    public int getOrder() { 
     return 100; 
    } 

    @AfterThrowing(pointcut = "execution(public * *(..))", throwing = "ex") 
    public void intercept(RuntimeException ex) throws Exception { 
     //throw ServiceException 
     System.out.println("RE - " + ex.getClass()); 
     throw new IllegalAccessException("RE2"); 
    } 

} 
+0

Penso che il flusso qui sia diverso dal mio. Basta accedere e l'intercettore esegue tutto. Io, nel primo metodo, lancio un'altra eccezione che non corrisponde al secondo metodo. – Francesco

+0

Come ho scritto, non si condividono dettagli importanti. Ho provato a lanciare "IllegalArgumentException" ma era molto simile, ne parlerò in risposta ... – Betlista

1

Come sull'utilizzo di un @Around consigli? È possibile utilizzare semplicemente il tipo sicuro try-catch al suo interno, non è necessario utilizzare alcun instanceof o una riflessione.

Ecco alcuni esempi di codice che ho compilato utilizzando AspectJ anziché Spring AOP perché non sono un utente Spring. Il punto di collegamento dovrebbe essere lo stesso comunque.

classi di supporto:

package de.scrum_master.service; 

public class DatabaseException extends RuntimeException { 
    public DatabaseException(Throwable arg0) { 
     super(arg0); 
    } 
} 
package de.scrum_master.service; 

public class ServiceException extends RuntimeException { 
    public ServiceException(Throwable arg0) { 
     super(arg0); 
    } 
} 

applicazione Driver (pianura Java, nessuna necessità di utilizzare Primavera):

package de.scrum_master.service; 

import java.util.Random; 
import org.springframework.jdbc.datasource.init.ScriptParseException; 

public class Application { 
    private static final Random RANDOM = new Random(); 

    public static void main(String[] args) { 
     Application application = new Application(); 
     for (int i = 0; i < 10; i++) { 
      try { 
       application.doSomething(); 
      } 
      catch (Exception e) { 
       System.out.println(e); 
      } 
     } 
    } 

    public void doSomething() { 
     switch (RANDOM.nextInt(3)) { 
      case 1: throw new ScriptParseException("uh-oh", null); 
      case 2: throw new IllegalArgumentException("WTF"); 
      default: System.out.println("doing something"); 
     } 
    } 
} 

Aspetto:

package de.scrum_master.aspect; 

import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Pointcut; 
import org.springframework.dao.DataAccessException; 
import org.springframework.stereotype.Component; 
import de.scrum_master.service.DatabaseException; 
import de.scrum_master.service.ServiceException; 

@Aspect 
@Component 
public class ExceptionWrapperInterceptor { 
    @Pointcut("within(*..service..*) && execution(* *(..))") 
    public void onlyServiceClasses() {} 

    @Around("onlyServiceClasses()") 
    public Object intercept(ProceedingJoinPoint thisJoinPoint) { 
     try { 
      return thisJoinPoint.proceed(); 
     } 
     catch (DataAccessException dae) { 
      throw new DatabaseException(dae); 
     } 
     catch (RuntimeException re) { 
      throw new ServiceException(re); 
     } 
    } 
} 

log della console:

doing something 
de.scrum_master.service.DatabaseException: org.springframework.jdbc.datasource.init.ScriptParseException: Failed to parse SQL script from resource [<unknown>]: uh-oh 
doing something 
de.scrum_master.service.DatabaseException: org.springframework.jdbc.datasource.init.ScriptParseException: Failed to parse SQL script from resource [<unknown>]: uh-oh 
doing something 
de.scrum_master.service.ServiceException: java.lang.IllegalArgumentException: WTF 
de.scrum_master.service.ServiceException: java.lang.IllegalArgumentException: WTF 
de.scrum_master.service.ServiceException: java.lang.IllegalArgumentException: WTF 
de.scrum_master.service.ServiceException: java.lang.IllegalArgumentException: WTF 
doing something