1. Introduction

In this article, you will learn about the After advice in Spring AOP which runs after the matched Joinpoint exits its execution. After advice is declared using the @After annotation. It is also known as After finally advice as it gets invoked irrespective of successful exit or abruption due to exception.

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

2. After (finally) advice execution – @After

After advice is also known as After finally advice as the advice is executed either the method exits successfully or terminates its execution by throwing an Exception. Therefore, After advice must be prepared to handle normal as well as exception return condition. A simple example is shown below.

Java
  @After("execution(* com.jbd.saop.after.dao.*.*(..))")
  public void logResults(JoinPoint joinPoint){
    System.out.println("\nAfter advice executed: "
        + joinPoint.getSignature().toShortString());
  }

It is typically used for releasing resources and similar purposes.

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 com.jbd.saop.after.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 com.jbd.saop.after;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

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

The pointcut expression matches with all methods of Dao class. If any of the Dao class methods throw a RuntimeException, it will be matched with both the advices. However, if any of the methods exits successfully only the logResults() will get executed.

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

@Service
@Aspect
public class AllAfterAspects {

  @Pointcut("execution(* com.jbd.saop.after.dao.*.*(..))")
  private void allDaoMethods(){}
  
  @AfterThrowing(
      pointcut = "allDaoMethods()",
      throwing = "exception")
  public void logDaoExceptions(JoinPoint joinPoint, RuntimeException exception) {
    System.out.println("After Exception executed: "
        + joinPoint.getSignature().toShortString());
    System.out.println("Exception details: " + exception);
  }
  
  @After("allDaoMethods()")
  public void logResults(JoinPoint joinPoint){
    System.out.println("\nAfter advice executed: "
        + joinPoint.getSignature().toShortString());
  }
}
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 com.jbd.saop.after;
import com.jbd.saop.after.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 TestAfterAdvice {

  @Autowired
  private UserRepository userRepository;

  @Test
  public void testAfterThrows() {
    //Assert exception
    Assertions.assertThrows(RuntimeException.class, () -> {
      userRepository.add(null);
      userRepository.delete(null);
    });
  }

  @Test
  public void testAfter() {
    //Assert not exception
    Assertions.assertDoesNotThrow(() -> {
      userRepository.delete("alexa");
    });
  }
}

The test cases should pass with the following output in the console. The order could be different.

Output:

After advice executed: UserRepository.add(..)
After Exception executed: UserRepository.add(..)
Exception details: java.lang.RuntimeException: username is null

User deleted: alexa
After advice executed: UserRepository.delete(..)

As you can see in the output, both the advices (@After and @AfterThrowing) are invoked when the add() method throws RuntimeException. Only the @After advice runs after the delete() completes execution.

Conclusion:

I hope to have given you a good understanding of the @After – After Advice in Spring AOP. In a practical use case, you may want to use this to perform resource deallocation type work. You can download the complete working code from the GitHub repo.


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