1. Introduction:

After returning advice gets invoked after a matched method finishes its execution and returns a value. You already know, the name for such a matched method is Joinpoint. You can declare an After returning advice using the @AfterReturning annotation.

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. Execution of @AfterReturning Advice

The @AfterReturning advice is executed after the Joinpoint method returns normally. We are only referring to method execution to be returned normally, the matched method can have any return type.

Java
@Configuration
@Aspect
public class UppercaseAspect {

  @AfterReturning("c.jbd.saop.ar.aspect.DaoExpressions.findMethod()")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
  //advice body
  }
}

Accessing the return value

You may need to access the actual value that was returned from the Joinpoint. You can use the @AfterReturning that binds the returning value to get that access. The below example shows accessing the return value and Joinpoint.

Java
@Configuration
@Aspect
public class UppercaseAspect {

  @AfterReturning(
      pointcut = "c.jbd.saop.ar.aspect.DaoExpressions.findMethod()",
      returning = "result")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
   //Advice body goes here
  }
}
  • The name used in the returning attribute must correspond to the name of a parameter in the advice method.
  • When a method execution returns, the return value is passed to the advice method.

3. Example of After returning advice – @AfterReturning

Problem statement – Let’s say we have a User management app. We need to make sure that the uppercased username is always returned from the userDao. Will use the @AfterReturning to manipulate the actual return values from the UserRepository:find() method.

Step-1: Add the dependencies

Create a maven project and add the following dependencies in the pom.xml, if you are adding the dependencies manually.

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, User 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.ar.dao;
import c.jbd.saop.ar.pojo.User;
import org.springframework.stereotype.Repository;

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

  //find an User
  public User find(String username) {
    if (username == null) {
      throw new RuntimeException("username is null", new NullPointerException());
    }
    return new User(username, "[email protected]");
  }

  //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.ar.pojo;
import java.util.StringJoiner;

//The User pojo
public class User {

  private String username;
  private String email;

  public User(String username, String email){
    this.email = email;
    this.username = username;
  }
  public String getUsername() {
    return username;
  }
  public User setUsername(String username) {
    this.username = username;
    return this;
  }
  public String getEmail() {
    return email;
  }
  public User setEmail(String email) {
    this.email = email;
    return this;
  }
  @Override
  public String toString() {
    return new StringJoiner(", ", User.class.getSimpleName() + "[", "]")
        .add("username='" + username + "'")
        .add("email='" + email + "'")
        .toString();
  }
}
Java
package c.jbd.saop.ar;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

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

I will keep the Pointcut expressions and Advice method in separate classes. I just tried to show you the combining and sharing of the expressions. The entire expression could be simplified as "execution(* c.jbd.saop.ar.dao.UserRepository.find(..))".

Java
package c.jbd.saop.ar.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class DaoExpressions {

  //Match with only UserRepository
  @Pointcut("within(c.jbd.saop.ar.dao.UserRepository)")
  public void userDao() {}

  //Match with any find() method
  @Pointcut("execution(* find(..))")
  public void findMethod() {}

  @Pointcut("userDao() && findMethod()")
  public void userDaoFind(){}
}
Java
package c.jbd.saop.ar.aspect;
import c.jbd.saop.ar.pojo.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;

@Configuration
@Aspect
public class UppercaseAspect {

  @AfterReturning(
      pointcut = "c.jbd.saop.ar.aspect.DaoExpressions.findMethod()",
      returning = "result")
  public void uppercaseUsername(JoinPoint joinPoint, User result){
    System.out.println("After method - " +
        joinPoint.getSignature().toShortString());
    System.out.println("original result:" + result);
    if (result.getUsername() != null){
      result.setUsername(result.getUsername().toUpperCase());
    }
    System.out.println("final result: " + result);
  }
}
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.ar;
import c.jbd.saop.ar.dao.UserRepository;
import c.jbd.saop.ar.pojo.User;
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 TestAfterReturning {

  @Autowired
  private UserRepository userRepository;

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

  @Test
  public void testFindUser(){
    String username = "JsTobigdata";
    User user = userRepository.find(username);
    Assertions.assertEquals(user.getUsername(), username.toUpperCase());
    Assertions.assertNotEquals(user.getUsername(), username);
  }
}

The tests should pass with the following output in the console.

Output:

After method - UserRepository.find(..)
original result:User[username='JsTobigdata', email='[email protected]']
final result: User[username='JSTOBIGDATA', email='[email protected]']

Conclusion:

I explained about After returning advice in Spring AOP using @AfterReturning. You can find the complete code example in the GitHub repo. Next, we will learn about the @AfterThrowing advice.


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