2016-01-05 209 views
5

我想使用AOP来拦截在服务层中抛出的所有运行时异常,并将其重新抛出为域例外。@AfterThrowing不能按预期工作

@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是不优雅,我;-)

感谢 弗朗西斯

+0

通过 “方法不对” 你的意思是,这两种方法都执行的,对不对? – Betlista

+0

不。只有'拦截(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用户,我整理了一些示例代码。无论如何,切入点应该是一样的。

Helper类:

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