2016-01-05 20 views
5

Chcę użyć AOP do przechwycenia wszystkich wyjątków wykonawczych wrzucanych do warstwy usługi i ponownego wyrzucenia jako wyjątków domeny.@Po rzutowaniu nie działa zgodnie z oczekiwaniami.

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

} 

Problem polega na tym, że z podklasy DataAccessException, środowisko wykonawcze wykonać niewłaściwy sposób. Czy jest to eleganckie rozwiązanie?

Wiosna Wersja: 4.2.4.RELEASE

PS: Jedna metoda rodzajowa (czytaj od innych pytań) z wieloma instanceof nie jest eleganckie dla mnie ;-)

Dzięki Francesco

+0

Przez "niewłaściwy sposób" masz na myśli, że obie metody są wykonywane, prawda? – Betlista

+0

Nie. Tylko metoda "przechwytuj (RuntimeException)". – Francesco

+0

Czy możesz sprawdzić moją odpowiedź i podzielić się tym, co różni się w twoim przypadku? Nie udostępniałeś wersji Spring, definicji 'onlyServiceClasses' i innych szczegółów ... – Betlista

Odpowiedz

3

wierzę, że oczekiwanie jest źle (to jedyny sposób przechwytują będzie pasował podobnie jak przy przeciążaniu metod).

Ale podczas RuntimeException jest rodzicem DataAccessException obie metody są wykonywane ...

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

    } 
} 

w dzienniku znajduje się:

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

Maven zależności:

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

From Spring documentation:

Gdy dwa rad zdefiniowanego w tym samym aspekcie zarówno potrzebne do uruchomienia w tym samym punkcie przyłączenia, kolejność jest niezdefiniowane (ponieważ nie ma sposobu, aby odzyskać kolejność zgłoszenia za pośrednictwem refleksji na javac- skompilowane klasy). Rozważ zapadanie się takich metod doradczych w jedną metodę porad na punkt złączenia w każdej klasie aspektu lub zmodyfikuj porady na osobne klasy aspektu - które można zamówić na poziomie aspektu.

Kiedy próbowałem następujące zmiany 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 
    } 

} 

dziennika zmienione na:

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

i kiedy zmodyfikowany Exception dostałam:

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 
    } 

} 

dziennik był

DAE 
RE - class java.lang.NullPointerException 

Wierzę, że rozwiązanie do "problem" jest mieć dwa aspekty zamiast jednego i określić kolejność:

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 
    } 

} 

i

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

Myślę, że przepływ tutaj różni się od mojego. Po prostu rejestruj się, a przechwytujący wykonuje wszystkie. W pierwszej metodzie wyrzucam inny wyjątek, który nie pasuje do drugiej metody. – Francesco

+0

Jak już pisałem, nie udostępniasz ważnych szczegółów. Próbowałem rzucić 'IllegalArgumentException' ale było bardzo podobne, wspomnę o tym w odpowiedzi ... – Betlista

1

Jak o użyciu @Around Rada? Możesz po prostu użyć typu bezpiecznego try-catch w tym, nie musisz używać żadnego instanceof lub odbicia.

Oto przykładowy kod, który skompilowałem przy użyciu AspectJ zamiast Spring AOP, ponieważ nie jestem użytkownikiem Spring. Punkt przecięcia powinien być taki sam.

zajęcia pomocnicze:

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

aplikacja kierowcy (zwykły Java, nie trzeba używać wiosna):

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

Aspekt: ​​

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

dziennika konsoli:

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 
Powiązane problemy