我想使用AOP拦截服务层中抛出的所有运行时异常,并重新抛出域异常。
@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 :(得分: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>
当在同一方面定义的两条建议都需要在同一个连接点上运行时,排序是未定义的(因为没有办法通过反射为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");
}
}
答案 1 :(得分:2)
使用@Around
建议怎么样?您可以在其中使用类型安全的try-catch
,无需使用任何instanceof
或反射。
以下是一些使用AspectJ而不是Spring AOP编译的示例代码,因为我不是Spring用户。无论如何,切入点应该是相同的。
助手类:
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