2016-01-05 2 views
5

Я хочу использовать AOP, чтобы перехватывать все исключения во время выполнения, сброшенные в сервисный уровень, и ретронировать как исключения для домена.@AfterTrowing не работает должным образом

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

} 

Проблема здесь состоит в том, что, с подклассом DataAccessException, среда выполнения неправильный метод. Это изящное решение?

Spring Версия: 4.2.4.RELEASE

P.S. Один общий метод (чтение из других вопросов) с большим количеством InstanceOf не элегантный для меня ;-)

Благодаря Francesco

+0

Под "неправильный метод" вы имеете в виду, что оба метода выполняются, не так ли? – Betlista

+0

Нет. Только метод 'intercept (RuntimeException). – Francesco

+0

Можете ли вы проверить мой ответ и поделиться тем, что в вашем случае отличается? Вы не использовали Spring версию, определение 'onlyServiceClasses' и другие детали ... – Betlista

ответ

3

Я считаю, что ваши ожидания не так (что только один метод перехвата будет соответствовать аналогично перегрузке метода).

Но в то время как RuntimeException является родителем DataAccessException оба метода выполняются ...

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

    } 
} 

и в журнале есть:

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

Maven зависимостей:

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

Когда две части советов, определенных в том же аспекте, как нужно работать с тем же точкой соединения, порядок не определена (так как нет никакого способа, чтобы получить заказ декларации через отражения для javac- скомпилированные классы). Подумайте о сворачивании таких советов в один советский метод для каждой точки соединения в каждом классе аспектов или реорганизуйте советы в отдельные классы аспектов, которые можно заказать на уровне аспект.

Когда я попытался следующие модификации 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 
    } 

} 

журнал изменен на:

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

и когда изменен, чтобы Exception я получил:

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 
    } 

} 

бревно было

DAE 
RE - class java.lang.NullPointerException 

Я считаю, что решение вашей «проблемы», чтобы иметь два аспекта вместо одного и определяют порядок:

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 
    } 

} 

и

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

Я думаю, что поток здесь отличается от моего. Вы просто регистрируетесь, и перехватчик выполняет все. Я, в первом методе, выкидывает другое исключение, которое не совпадает со вторым методом. – Francesco

+0

Как я уже писал, вы не делитесь важными деталями. Я попытался выбросить 'IllegalArgumentException', но это было очень похоже, я упомянул об этом в ответ ... – Betlista

1

Как об использовании @Around совет? Вы можете просто использовать безопасный тип try-catch там, не нужно использовать никаких instanceof или отражения.

Вот пример кода, который я скомпилировал с использованием AspectJ вместо Spring AOP, потому что я не пользователь Spring. В любом случае pointcut должен быть одинаковым.

Вспомогательные классы:

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

Драйвер приложения (простой Java, нет необходимости использовать Spring):

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

Формат:

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

журнала консоли:

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 
Смежные вопросы