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.

Java
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.

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>

If you are using , Spring boot jus add the following:

XML
<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>
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.

Java
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;
  }
}
Java
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;
  }
}
Java
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.

Java
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.

Java
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 the delete() 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.

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>
Java
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 @Testcase to the TestBeforeAdvice class and observe the output.

Java
@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.


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

Table of Contents