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.
@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.
@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
<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.
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; } }
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(); } }
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(..))"
.
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(){} }
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.
<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 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.
Leave A Comment