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 dependenciesAdd 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>
If you are using , Spring boot jus add the following:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
...
</dependencies>
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 {
}
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 {
}
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.
Step-5: Test the exampleInstead 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.
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");
}
}
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.