@AfterThrowing does not work as expected

I want to use AOP to catch all runtime exceptions thrown at the service level and reconstruct as exceptions for the domain.

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

The problem is that with a subclass of DataAccessException, the runtime is executing the wrong method. Is this an elegant solution?

Spring Version: 4.2.4.RELEASE

PS One common method (read from other questions) with a large number of instances is not elegant for me; -)

Thanks Francesco

+5
source share
2 answers

I believe that your expectation is incorrect (that only one interception method will correspond similarly to method overloading).

But while RuntimeException is the parent of a DataAccessException , both methods are executed ...

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

Myspect

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

and in the magazine:

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

Maven Dependencies:

  <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 the Spring documentation :

When two tips defined in the same aspect, both must be executed at the same junction point, the order is undefined (since there is no way to get the declaration order through reflection for javac-compiled classes). Consider folding these tips into one consultation method at each junction point in each aspect class, or reorganizing the tips into separate aspect classes that can be ordered at the aspect level.

When I tried the following modification of 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 } } 

log changed to:

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

and when changing to Exception I got:

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

the magazine was

 DAE RE - class java.lang.NullPointerException 

I believe the solution to your โ€œproblemโ€ is to have two Aspects instead of one and determine the order:

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

and

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

How about using @Around ? You can just use the safe try-catch type, no need to use instanceof or reflection.

Here is a sample code that I compiled using AspectJ instead of Spring AOP, because I am not a Spring user. In any case, the pointcut must be the same.

Helper classes:

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

Driver application (plain Java, no need to use 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"); } } } 

Format:

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

Console Log:

 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 
+1
source

Source: https://habr.com/ru/post/1239900/


All Articles