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.
- Before advice –
@Before
- After returning –
@AfterReturning
- After throwing –
@AfterThrowing
- After (finally) advice –
@After
- Around advice –
@Around
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.
@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.
Create a maven project and add the following dependencies in the pom.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>
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.
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;
}
}
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 {
}
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.
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());
}
}
Finally, we will test the example to see its output. Add the test dependencies in the pom.xml
before running the test cases.
<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
.
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.