1. Introduction:

After Throwing advice gets invoked after a matched method finishes/aborts its execution by throwing an Exception. You already know, the name for such a matched method is Joinpoint. You can declare an After throwing advice using the @AfterThrowing annotation.

You already know about the 5 types of advice from the previous article as listed below.

2. After throwing advice execution

You can declare an After throwing advice as shown below. Optionally you can get the Exception that is thrown by the method execution and the Joinpoint details.

Java
package com.jbd.saop.after.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;

@Service
@Aspect
public class AllAfterAspects {

  @AfterThrowing(
      pointcut = "execution(* c.jbd.saop.at.dao.*.*(..))",
      throwing = "exception")
  public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
    System.out.println("Exception at: " + joinPoint.getSignature().toShortString());
    System.out.println("Exception details: " + exception);
  }
}

throwing clause also restricts matching to only those method executions that throw an exception of the specified type. In this case, it is RuntimeException.

3. Example of After throwing advice – @AfterThrowing

Problem statement – Let’s say we have a User management app. We want to log all the exceptions thrown by the methods for any reasons. We will use the same example from the previous article and pass null values to add() and delete() methods.

Step-1: Add the dependencies

Create a maven project and add the following dependencies in the pom.xml

XML
<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <!-- Step-1 Add the dependencies -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.2.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>
<dependencies>
Step-2: Create UserRepository

We will apply the aspect to DAO classes. So we create the sample code – UserRepository and the Java configuration to register this class as below. As we discussed before, the annotation @EnableAspectJAutoProxy is used to enable the Spring-AOP.

Java
package c.jbd.saop.at.dao;
import org.springframework.stereotype.Repository;

//A very stupid demo repository
@Repository
public class UserRepository {

  //Add a user
  public UserRepository add(String username){
    if(username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    System.out.println("New user added: " + username);
    return this;
  }
 
  //Update an user
  public UserRepository update(String username, String email) {
    System.out.println("Update email: " + email);
    return this;
  }

  //Delete an user
  public boolean delete(String username){
    if (username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    System.out.println("User deleted: " + username);
    return true;
  }
}
Java
package c.jbd.saop.at;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("c.jbd.saop.at")
public class ApplicationConfig {
}
Step-3: Create Pointcut expressions and advice

The pointcut expression matches with all the Dao class methods. If any of the Dao class method throws an RuntimeException, it will be matched with this aspect and the code inside logDaoExceptions() will be executed.

Java
package c.jbd.saop.at.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;

@Configuration
@Aspect
public class ExceptionAspect {
 
  /**
   * Match with all dao methods.
   */
  @AfterThrowing(
      pointcut = "execution(* c.jbd.saop.at.dao.*.*(..))",
      throwing = "exception")
  public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
    System.out.println("Exception at: " + joinPoint.getSignature().toShortString());
    System.out.println("Exception details: " + exception);
  }
}
Step-4: Test the example

Finally, we will test the example to see its output. Add the test dependencies in the pom.xml before running the test cases.

XML
  <dependencies>
  ... 
   <!-- Test Dependencies-->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>5.5.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.2.RELEASE</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
...
<!-- Important for Jupiter-engine -->
<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.0.0-M3</version>
      </plugin>
    </plugins>
  </build>
</project>

Always remember to upgrade the maven-surefire-plugin to 3.0.0-M3 or higher when using Junit-jupiter-*.

Once you have added the test dependencies, create the Test class inside src\test.

Java
package c.jbd.saop.at;
import c.jbd.saop.at.dao.UserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

@SpringJUnitConfig(ApplicationConfig.class)
public class TestAfterThrowing {

  @Autowired
  private UserRepository userRepository;

  @Test
  public void notNull() {
    Assertions.assertNotNull(userRepository);
  }

  @Test
  public void testThrows() {
    //Assert throws RuntimeException
    Assertions.assertThrows(RuntimeException.class, () -> {
      userRepository.add(null);
      userRepository.delete(null);
    });
    //Assert does not throw exception
    Assertions.assertDoesNotThrow(() -> {
      userRepository.delete("alexa");
    });
  }
}

The test cases should pass with the following output in the console.

Output:

After method - Exception at: UserRepository.add(..)
Exception details: java.lang.RuntimeException: username is null
User deleted: alexa

Conclusion:

I hope to have given you a good understanding of the @AfterThrowing – After Throwing Advice in Spring AOP. In a practical use case, you may want to use this to perform LOGs or analytics on the exceptions. You can download the complete working code from the GitHub repo. Next, we will learn about the @After finally advice.

By |Last Updated: April 2nd, 2024|Categories: Spring Framework|

Table of Contents