Skip to content

Spring Boot application that implements the AOP multiple advice types

Notifications You must be signed in to change notification settings

andrecaiado/spring-boot-aop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Boot AOP project

This is a Spring Boot application that demonstrates how to use Aspect-Oriented Programming (AOP).

Contents

Dependency

To use Spring AOP in our Spring Boot application, the spring-boot-starter-aop dependency was added in thepom.xmlfile.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
</dependencies>

Aspect

Aspects are usually describe as being a module that encapsulates specific cross-cutting concerns in our application, e.g., logging, transaction management, security and others.

Since this is a demo project with a small scope and a low complexity, there aren't many cross-cutting concerns to implement aspects, thus, we will focus on the logging concern and create a logging aspect.

The logging aspect is created by creating a class annotated with @Aspect and @Component. The @Aspect annotation marks the class as an aspect, and the @Component annotation marks the class as a Spring bean.

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {
    // Advices are added here
}

To apply the aspect, AOP must be enabled in the Spring Boot application, for that, the @EnableAspectJAutoProxy annotation was added to the application main class.

@EnableAspectJAutoProxy
@SpringBootApplication
public class SpringBootAopApplication {
  // Main method
}

Advices

@Before

This advice type is executed before the method execution. It’s useful for tasks like logging, security, and transaction management.

In this project, the @Before advice is used to log the method name and its arguments before the method execution.

The pointcut of this advice targets all the methods called, with zero or more arguments, from the com.example.springaop.service package and its subpackages.

@Before("execution(* com.example.springbootaop.service.*.*(..))")
public void logBeforeService(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().toShortString();
    String args = Arrays.toString(joinPoint.getArgs());
    log.info("Executing method: " + methodName + " with arguments: " + args);
}

Example of the message logged when this advice is triggered:

2024-06-12T17:42:51.300+01:00  INFO 85990 --- [spring-boot-aop] [nio-8080-exec-2] c.e.springbootaop.aspect.LoggingAspect   : Executing method: EmployeeService.getEmployeeById(..) with arguments: [1]

@After

This advice is executed after the method execution. It’s similar to the @AfterReturning advice, but it runs regardless of the method outcome. It’s useful for tasks like cleaning up resources or finalizing operations.

In this project, the @After advice is used to log the method name after the method execution.

The pointcut of this advice targets all the methods called, with zero or more arguments, from the com.example.springaop.service package and its subpackages.

@After("execution(* com.example.springbootaop.service.*.*(..))")
public void logAfterService(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().toShortString();
    log.info("Method executed: " + methodName);
}

Example of the message logged when this advice is triggered:

2024-06-12T18:17:16.364+01:00  INFO 22937 --- [spring-boot-aop] [nio-8080-exec-1] c.e.springbootaop.aspect.LoggingAspect   : Method executed: EmployeeService.getEmployeeById(..)

@AfterReturning

This advice is executed after the method execution. It’s useful for tasks like cleaning up resources or finalizing operations.

In this project, the @AfterReturning advice will log the message HAPPY COMPANY ANNIVERSARY, after the method execution, if the joinedOn date day and month are the same as the current date day and month.

The pointcut of this advice targets the methods saveEmployee and updateEmployee from the com.example.springaop.service.EmployeeService class.

@AfterReturning(pointcut = "execution(* com.example.springbootaop.service.EmployeeService.saveEmployee(..)) || execution(* com.example.springbootaop.service.EmployeeService.updateEmployee(..))", returning = "employee")
public void afterReturningSaveEmployee(JoinPoint joinPoint, Employee employee) {
    LocalDate today = LocalDate.now();
    if (employee.getJoinedOn().getMonth() == today.getMonth() && employee.getJoinedOn().getDayOfMonth() == today.getDayOfMonth()) {
        log.info("HAPPY COMPANY ANNIVERSARY " + employee.getFirstName() + " " + employee.getLastName() + "!!!");
    }
}

Example of the message logged when this advice is triggered:

2024-06-12T18:32:31.330+01:00  INFO 39304 --- [spring-boot-aop] [nio-8080-exec-1] c.e.springbootaop.aspect.LoggingAspect   : HAPPY COMPANY ANNIVERSARY André Caiado!!!

@AfterThrowing

This advice is executed if the method throws an exception. It’s useful for tasks like logging, sending notifications, and handling exceptions.

In this project, the @AfterThrowing advice is used to log the object details if an exception occurs when saving or updating an employee. To test this advice, a condition was added to the EmployeeService to throw an exception if the employee's first name is John and the last name is Doe.

The pointcut of this advice targets the methods saveEmployee and updateEmployee in the com.example.springaop.service.EmployeeService class.

@AfterThrowing(pointcut = "execution(* com.example.springbootaop.service.EmployeeService.saveEmployee(..)) || execution(* com.example.springbootaop.service.EmployeeService.updateEmployee(..))", throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
    String methodName = joinPoint.getSignature().toShortString();
    log.error("Exception thrown by " + methodName + ": " + exception.getMessage());
    }

Example of the message logged when this advice is triggered:

2024-06-13T16:43:28.639+01:00 ERROR 99736 --- [spring-boot-aop] [nio-8080-exec-1] c.e.springbootaop.aspect.LoggingAspect   : Exception thrown by EmployeeService.saveEmployee(..): Invalid employee name

@Around

This is the most powerful advice type. It wraps around the method and can control its execution. We can modify input and output, perform additional actions before and after the method, and even prevent the method from executing. It’s useful for tasks like logging, security, and transaction management.

For this advice type, we implemented two advices:

  1. An advice to display a message before and after the method execution.

The pointcut of this advice targets all the methods called from the com.example.springaop.controller package and its subpackages.

@Around(value = "execution(* com.example.springbootaop.controller.*.*(..))")
public Object logAroundController(ProceedingJoinPoint joinPoint) throws Throwable {
    String methodName = joinPoint.getSignature().toShortString();
    log.info("Controller called: " + methodName);
    Object result = joinPoint.proceed();
    log.info("Controller executed: " + methodName);
    return result;
}

Example of the message logged when this advice is triggered:

2024-06-13T17:26:06.729+01:00  INFO 58075 --- [spring-boot-aop] [nio-8080-exec-2] c.e.springbootaop.aspect.LoggingAspect   : Controller called: EmployeeController.saveEmployee(..)
2024-06-13T17:26:06.820+01:00  INFO 58075 --- [spring-boot-aop] [nio-8080-exec-2] c.e.springbootaop.aspect.LoggingAspect   : Controller executed: EmployeeController.saveEmployee(..)
  1. An advice to measure the execution time of a method.

For this purpose we created a custom AOP annotation @LogExecutionTime.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

The pointcut of this advice targets all the methods annotated with @LogExecutionTime.

@Around("@annotation(com.example.springbootaop.aspect.LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - startTime;
    log.info(joinPoint.getSignature().toShortString() + " executed in " + executionTime + "ms");
    return result;
}

Example of the message logged when this advice is triggered:

2024-06-13T17:51:37.046+01:00  INFO 84567 --- [spring-boot-aop] [nio-8080-exec-1] c.e.springbootaop.aspect.LoggingAspect   : EmployeeController.getAllEmployees() executed in 256ms

About

Spring Boot application that implements the AOP multiple advice types

Topics

Resources

Stars

Watchers

Forks

Languages