In this tutorial, you will learn about the @Before advice in Spring AOP. Before proceeding with this tutorial, make sure you have a good understanding of the AOP terminologies. You already know about the 5 types of advice from the previous article as listed below.
- Before advice –
@Before
- Around advice –
@Around
- After returning –
@AfterReturning
- After throwing –
@AfterThrowing
- After (finally) advice –
@After
Execution of @Before Advice
A @Before advice
is executed before the method execution. In technical terms, an advice annotated with @Before is executed before the matched Join points methods get executed. Before advice can be applied to one or more matching Join points.
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BeforeExample { @Before("execution(* com.jbd.myapp.service.*.*(..)") public void doAccessCheck() { // ... } }
Steps to use @Before Advice annotation
Step-1: Add dependencies
Add the necessary dependencies to your pom.xml
. Technically you only need the aspectjweaver
and spring-aop
jars. As this is a spring application, so we will also need the spring-core
and spring-context
jars. Additionally, you can add the JUnit and spring-test jars to run the test cases.
<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: Crete the Sample classes
Create the sample classes in their respective packages so that we have the example codes to apply before-advice. Basically we will create 2 repository classes, each with add() and delete() methods.
package c.jbd.saop.gettingstarted.dao; import org.springframework.stereotype.Repository; @Repository public class ActorRepository { //Add an actor public ActorRepository add(String actorName) { if (actorName == null) { throw new RuntimeException("actorName is null", new NullPointerException()); } System.out.println("New Actor added: " + actorName); return this; } //Delete an actor public boolean delete(String actorName) { if (actorName == null) { throw new RuntimeException("actorName is null", new NullPointerException()); } System.out.println("Actor deleted: " + actorName); return true; } }
package c.jbd.saop.gettingstarted.dao; import org.springframework.stereotype.Repository; @Repository public class MovieRepository { //Add a movie method public MovieRepository add(String movieName) { if (movieName == null) { throw new RuntimeException("movieName is null", new NullPointerException()); } System.out.println("New movie added: " + movieName); return this; } //Delete a movie public boolean delete(String movieName) { if (movieName == null) { throw new RuntimeException("movieName is null", new NullPointerException()); } System.out.println("Movie deleted: " + movieName); return true; } }
package c.jbd.saop.gettingstarted.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @ComponentScan("c.jbd.saop.gettingstarted") public class ApplicationConfig { }
Step-3: Enable AspectJ
Enable the AspectJ auto proxy by adding @EnableAspectJAutoProxy
to the ApplicationConfig
class as below. Basically, this will enable Spring-AOP in the application.
ackage c.jbd.saop.gettingstarted.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration //Step-3 - Add the annotation @EnableAspectJAutoProxy @ComponentScan("c.jbd.saop.gettingstarted") public class ApplicationConfig { }
Step-4: Declare advice class
Now let’s create a class named BeforeAdvice
as below. It is important to annotate the aspect class with @Aspect
annotation to mark this as an aspect. We also need this to be picked by Spring, so use the @Configurartion
annotation. We will revisit the expression inside the @Before
annotation, which is also known as Pointcut.
package c.jbd.saop.gettingstarted.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.context.annotation.Configuration; @Configuration @Aspect //Important to say this is an Aspect config class public class BeforeAdvice { //execution(RETURN_TYPE PACKAGE.CLASS.METHOD(..PARAMETER_LIST)) //execution(* PACKAGE.*.*(..)) @Before("execution(* c.jbd.saop.gettingstarted.dao.*.add(..))") public void allDaoAddMethods(JoinPoint joinPoint) { System.out.println("Intercepted method: " + joinPoint); System.out.println("Arguments: " + joinPoint.getArgs()); System.out.println(joinPoint.getTarget()); } @Before("execution(* c.jbd.saop.gettingstarted.dao.*.*(..))") public void allDaoAnyMethod(JoinPoint joinPoint) { System.out.println("Intercepted method: " + joinPoint); System.out.println("Arguments: " + joinPoint.getArgs()); System.out.println(joinPoint.getTarget()); } }
- We created two Before aspects, each with separate Pointcut expression.
- The first one is applied to
add() methods
of all the repository classes, and the second aspect is applied to all the methods of repository classes. - In other words, the second aspect will be called for
add()
as well as thedelete()
methods of ActorRepository and MovieRepository.
Instead of the @Configuration annotation, we can also make use of @Component for aspect classes. However, as we ourselves don’t need an instance of this, spring recommends using the @Configuration.
Step-5: Test the example
Finally, we will test the example to see its output. Will use the normal spring-test
with junit-jupiter-api
. So make sure you have the test dependencies as shown below.
. <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>
package c.jbd.saop.gettingstarted; import c.jbd.saop.gettingstarted.config.ApplicationConfig; import c.jbd.saop.gettingstarted.dao.ActorRepository; import c.jbd.saop.gettingstarted.dao.MovieRepository; 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 TestBeforeAdvice { @Autowired private ActorRepository actorRepository; @Autowired private MovieRepository movieRepository; @Test public void testAddAspects() { actorRepository.add("Hrithik Roshan"); movieRepository.add("Sholey"); } }
Output:
Intercepted method: execution(ActorRepository c.jbd.saop.gettingstarted.dao.ActorRepository.add(String)) Arguments: Hrithik Roshan c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e Intercepted method: execution(ActorRepository c.jbd.saop.gettingstarted.dao.ActorRepository.add(String)) Arguments: Hrithik Roshan c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e New Actor added: Hrithik Roshan Intercepted method: execution(MovieRepository c.jbd.saop.gettingstarted.dao.MovieRepository.add(String)) Arguments: Sholey c.jbd.saop.gettingstarted.dao.MovieRepository@61078690 Intercepted method: execution(MovieRepository c.jbd.saop.gettingstarted.dao.MovieRepository.add(String)) Arguments: Sholey c.jbd.saop.gettingstarted.dao.MovieRepository@61078690 New movie added: Sholey
You can see in the above output, for add() methods of both Repository BeforeAdvice:allDaoAddMethods()
and BeforeAdvice:allDaoAnyMethod()
gets executed before the actual method calls.
Step-6: Additional tests
Now let’s just add another @Test
case to the TestBeforeAdvice
class and observe the output.
@SpringJUnitConfig(ApplicationConfig.class) public class TestBeforeAdvice { ... ... @Test public void testDeleteAspects() { actorRepository.delete("John Doe"); movieRepository.delete("Abc"); } }
Intercepted method: execution(boolean c.jbd.saop.gettingstarted.dao.ActorRepository.delete(String)) Arguments: John Doe c.jbd.saop.gettingstarted.dao.ActorRepository@6e521c1e Actor deleted: John Doe Intercepted method: execution(boolean c.jbd.saop.gettingstarted.dao.MovieRepository.delete(String)) Arguments: Abc c.jbd.saop.gettingstarted.dao.MovieRepository@61078690 Movie deleted: Abc
For both the delete() calls, only the BeforeAdvice:allDaoAnyMethod()
gets executed as this is the only aspect that matches.
Conclusion:
I have tried to present a simple example to understand the working of @Before advice in spring AOP. Next, we will explore the Pointcut expression and @Pointcut annotation to reuse the expressions. Download the complete code example from GitHub.
Leave A Comment