Spring AOP Tutorial with Annotations

In this tutorial, we will learn the usage of common Spring AOP annotations with an example.

Learn Spring AOP concepts and terminology with the step-by-step examples at https://www.javaguides.net/2019/05/understanding-spring-aop-concepts-and-terminology-with-example.html

AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code without modification of the code itself. Instead, we declare separately which code is to modify.

We will learn how to integrate AspectJ annotations with the Spring AOP framework. We will create a simple spring boot application, add a logging aspect, and then invoke aspect methods based on pointcuts information passed in AspectJ annotations.
Common AspectJ annotations :
  • @Before – Run before the method execution
  • @After – Run after the method returned a result
  • @AfterReturning – Run after the method returned a result, intercept the returned result as well.
  • @AfterThrowing – Run after the method throws an exception
  • @Around – Run around the method execution, combine all three advices above.
Let's first look at the overview of each AspectJ annotation and then we will develop a complete example to demonstrate the usage of AspectJ @Before, @After, @AfterReturning, @AfterThrowing, and @Around annotations.

AspectJ @Before Annotation Usage 

@Before annotated methods run exactly before all methods matching with pointcut expression. Here is a sample usage of @Before annotation:
@Aspect
@Component
public class LoggingAspect {

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 /**
  * Run before the method execution.
  * @param joinPoint
  */
 @Before("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.addEmployee(..))")
 public void logBefore(JoinPoint joinPoint) {
  log.debug("logBefore running .....");
  log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
    joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
  
 }
}
  • @Aspect - Indicates that this is an Aspect
  • @Component - This annotation mark the beans as Spring’s managed components
  • @Before annotated methods run exactly before all methods matching with a pointcut expression

AspectJ @After Annotation Usage

@After annotated methods run exactly after all methods matching with pointcut expression. You can use @After annotation in the below manner.
@Aspect
@Component
public class LoggingAspect {

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 /**
  * Run after the method returned a result.
  * @param joinPoint
  */
 @After("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.addEmployee(..))")
 public void logAfter(JoinPoint joinPoint) {
  log.debug("logAfter running .....");
  log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
    joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
 }
}
  • @Aspect - Indicates that this is an Aspect
  • @Component - This annotation mark the beans as Spring’s managed components
  • @After annotated methods run exactly after all methods matching with a pointcut expression

AspectJ @Around Annotation Usage

@Around annotated methods run before and after all methods matching with pointcut expression. Here is a sample usage of @Around annotation:
@Aspect
@Component
public class LoggingAspect {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * Run around the method execution.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.getEmployeeById(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.debug("logAround running .....");
        if (log.isDebugEnabled()) {
            log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
        }
        try {
            Object result = joinPoint.proceed();
            if (log.isDebugEnabled()) {
                log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
                    joinPoint.getSignature().getName(), result);
            }
            return result;
        } catch (IllegalArgumentException e) {
            log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
                joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
            throw e;
        }

    }
}
  • @Aspect - Indicates that this is an Aspect
  • @Component - This annotation mark the beans as Spring’s managed components
  • @Around annotated methods run before and after all methods matching with a pointcut expression

AspectJ @AfterReturning Annotation Usage

Run after the method returned a result, intercept the returned result as well. Here is a usage of @AfterReturning annotation:
@Aspect
@Component
public class LoggingAspect {

 private final Logger log = LoggerFactory.getLogger(this.getClass());

 /**
  * Run after the method returned a result, intercept the returned result as well.
  * @param joinPoint
  * @param result
  */
 @AfterReturning(pointcut = "execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.deleteEmployee(..))", returning = "result")
 public void logAfterReturning(JoinPoint joinPoint, Object result) {
  log.debug("logAfterReturning running .....");
  log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
    joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));

 }
}
  • @Aspect - Indicates that this is an Aspect
  • @Component - This annotation mark the beans as Spring’s managed components
  • @AfterReturning - Run after the method returned a result, intercept the returned result as well

AspectJ @AfterThrowing Annotation Usage

@AfterThrowing annotated methods run after the method (matching with pointcut expression) exits by throwing an exception. Here is a simple example:
@Aspect
@Component
public class LoggingAspect {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * Advice that logs methods throwing exceptions.
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */

    @AfterThrowing(pointcut = "execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.updateEmployee(..))", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        log.debug("logAfterThrowing running .....");
        log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
            joinPoint.getSignature().getName(), error.getCause() != null ? error.getCause() : "NULL");
    }
}
  • @Aspect - Indicates that this is an Aspect
  • @Component - This annotation mark the beans as Spring’s managed components
  • @AfterThrowing annotated methods run after the method (matching with pointcut expression) exits by throwing an exception.

Develop Complete Spring AOP Example with AspectJ Annotations

Let's create a complete step-by-step CRUD spring boot application with Spring AOP.

Tools and Technologies Used

  • Spring Boot - 2.1.4 RELEASE
  • Spring Framework - 5.1.6 RELEASE
  • Spring Data JPA - 2.16 RELEASE
  • Hibernate - 5.3.9.Final
  • Thymeleaf - 3.0.11 RELEASE
  • Maven - 3.2+
  • IDE - Eclipse or Spring Tool Suite (STS)
  • H2 Database - 1.4.99

Development Steps

  1. Creating a Spring Boot Application
  2. Project Structure
  3. Maven Dependencies - Pom.xml
  4. Domain Layer
  5. The Controller Layer
  6. The Service Layer
  7. The Repository Layer
  8. The Logging Aspect
  9. Exception Handling
  10. Running the Application
  11. Spring Rest Client
  12. Conclusion

1. Creating a Spring Boot Application

There are many ways to create a Spring Boot application. You can refer to below articles to create a Spring Boot application.
Refer next step to create the project packaging structure.

2. Project Structure

Following is the package or project structure for your reference - 

3. Maven Dependencies - Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.guides.springboot2</groupId>
    <artifactId>springboot2-springaop-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>springboot2-springaop-example</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath />
        <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4. Domain Layer

Let's create a simple Employee JPA entity class with the following code in it:
package net.guides.springboot2.springboot2jpacrudexample.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "employees")
public class Employee {

    private long id;
    private String firstName;
    private String lastName;
    private String emailId;

    public Employee() {

    }

    public Employee(String firstName, String lastName, String emailId) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailId = emailId;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }

    @Column(name = "first_name", nullable = false)
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Column(name = "last_name", nullable = false)
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Column(name = "email_address", nullable = false)
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", emailId=" + emailId +
            "]";
    }
}

5. The Controller Layer

Let's first create a Spring rest controller class and then we will add the logging aspect to it:
package net.guides.springboot2.springboot2jpacrudexample.controller;

import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNotFoundException;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService;

@RestController
@RequestMapping("/api/v1")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    @GetMapping("/employees")
    public List < Employee > getAllEmployees() {
        return employeeService.getAllEmployees();
    }

    @GetMapping("/employees/{id}")
    public ResponseEntity < Employee > getEmployeeById(@PathVariable(value = "id") Long employeeId)
    throws ResourceNotFoundException {
        Employee employee = employeeService.getEmployeeById(employeeId)
            .orElseThrow(() - > new ResourceNotFoundException("Employee not found for this id :: " + employeeId));
        return ResponseEntity.ok().body(employee);
    }

    @PostMapping("/employees")
    public Employee createEmployee(@Valid @RequestBody Employee employee) {
        return employeeService.createEmployee(employee);
    }

    @PutMapping("/employees/{id}")
    public ResponseEntity < Employee > updateEmployee(@PathVariable(value = "id") Long employeeId,
        @Valid @RequestBody Employee employeeDetails) throws ResourceNotFoundException {
        Employee updatedEmployee = employeeService.updateEmployee(employeeId, employeeDetails);
        return ResponseEntity.ok(updatedEmployee);
    }

    @DeleteMapping("/employees/{id}")
    public Map < String, Boolean > deleteEmployee(@PathVariable(value = "id") Long employeeId)
    throws ResourceNotFoundException {
        return employeeService.deleteEmployee(employeeId);
    }
}

6. The Service Layer

Let's first create EmployeeService class and then we will add the logging aspect to it:
package net.guides.springboot2.springboot2jpacrudexample.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNotFoundException;
import net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import net.guides.springboot2.springboot2jpacrudexample.repository.EmployeeRepository;

/**
 * Employee Service
 * @author Ramesh
 *
 */
@Service
public class EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;

    public List < Employee > getAllEmployees() {
        return employeeRepository.findAll();
    }

    public Optional < Employee > getEmployeeById(Long employeeId)
    throws ResourceNotFoundException {
        return employeeRepository.findById(employeeId);
    }

    public Employee createEmployee(Employee employee) {
        return employeeRepository.save(employee);
    }

    public Employee updateEmployee(Long employeeId,
        Employee employeeDetails) throws ResourceNotFoundException {
        Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() - > new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

        employee.setEmailId(employeeDetails.getEmailId());
        employee.setLastName(employeeDetails.getLastName());
        employee.setFirstName(employeeDetails.getFirstName());
        final Employee updatedEmployee = employeeRepository.save(employee);
        return updatedEmployee;
    }

    public Map < String, Boolean > deleteEmployee(Long employeeId)
    throws ResourceNotFoundException {
        Employee employee = employeeRepository.findById(employeeId)
            .orElseThrow(() - > new ResourceNotFoundException("Employee not found for this id :: " + employeeId));

        employeeRepository.delete(employee);
        Map < String, Boolean > response = new HashMap < > ();
        response.put("deleted", Boolean.TRUE);
        return response;
    }
}

7. The Repository Layer

Let's first create EmployeeRepository and then we will add the logging aspect to it:
package net.guides.springboot2.springboot2jpacrudexample.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import net.guides.springboot2.springboot2jpacrudexample.model.Employee;

@Repository
public interface EmployeeRepository extends JpaRepository < Employee, Long > {

}

8. The Logging Aspect

Now, let's create an Aspect for logging execution. This LoggingAspect class provides AspectJ @Before, @After, @AfterReturning, @AfterThrowing, and @Around annotated methods:
package net.guides.springboot2.springboot2jpacrudexample.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * Aspect for logging execution.
 * 
 * @author Ramesh Fadatare
 *
 */
@Aspect
@Component
public class LoggingAspect {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    /**
     * Run before the method execution.
     * @param joinPoint
     */
    @Before("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.addEmployee(..))")
    public void logBefore(JoinPoint joinPoint) {
        log.debug("logBefore running .....");
        log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
            joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));

    }

    /**
     * Run after the method returned a result.
     * @param joinPoint
     */
    @After("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.addEmployee(..))")
    public void logAfter(JoinPoint joinPoint) {
        log.debug("logAfter running .....");
        log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
            joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * Run after the method returned a result, intercept the returned result as well.
     * @param joinPoint
     * @param result
     */
    @AfterReturning(pointcut = "execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.deleteEmployee(..))", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        log.debug("logAfterReturning running .....");
        log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
            joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));

    }

    /**
     * Run around the method execution.
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.getEmployeeById(..))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.debug("logAround running .....");
        if (log.isDebugEnabled()) {
            log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()));
        }
        try {
            Object result = joinPoint.proceed();
            if (log.isDebugEnabled()) {
                log.debug("Exit: {}.{}() with result = {}", joinPoint.getSignature().getDeclaringTypeName(),
                    joinPoint.getSignature().getName(), result);
            }
            return result;
        } catch (IllegalArgumentException e) {
            log.error("Illegal argument: {} in {}.{}()", Arrays.toString(joinPoint.getArgs()),
                joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
            throw e;
        }

    }

    /**
     * Advice that logs methods throwing exceptions.
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */

    @AfterThrowing(pointcut = "execution(* net.guides.springboot2.springboot2jpacrudexample.service.EmployeeService.updateEmployee(..))", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        log.debug("logAfterThrowing running .....");
        log.error("Exception in {}.{}() with cause = {}", joinPoint.getSignature().getDeclaringTypeName(),
            joinPoint.getSignature().getName(), error.getCause() != null ? error.getCause() : "NULL");
    }
}

9. Exception Handling

Spring Boot provides a good default implementation for exception handling for RESTful Services. Let’s quickly look at the default Exception Handling features provided by Spring Boot.

Resource Not Present

Here's what happens when you fire a request to not resource found: http://localhost:8080/some-dummy-url
{
  "timestamp": 1512713804164,
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/some-dummy-url"
}
That's a cool error response. It contains all the details that are typically needed.

What happens when we throw an Exception?

Let’s see what Spring Boot does when an exception is thrown from a Resource. we can specify the Response Status for a specific exception along with the definition of the Exception with ‘@ResponseStatus’ annotation.
Let's create a ResourceNotFoundException.java class.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{

    private static final long serialVersionUID = 1L;

    public ResourceNotFoundException(String message){
        super(message);
    }
}

Customizing Error Response Structure

Default error response provided by Spring Boot contains all the details that are typically needed.
However, you might want to create a framework independent response structure for your organization. In that case, you can define a specific error response structure.
Let’s define a simple error response bean.
import java.util.Date;

public class ErrorDetails {
 private Date timestamp;
 private String message;
 private String details;

 public ErrorDetails(Date timestamp, String message, String details) {
  super();
  this.timestamp = timestamp;
  this.message = message;
  this.details = details;
 }

 public Date getTimestamp() {
  return timestamp;
 }

 public String getMessage() {
  return message;
 }

 public String getDetails() {
  return details;
 }
}
To use ErrorDetails to return the error response, let’s create a GlobalExceptionHandler class annotated with @ControllerAdvice annotation. This class handles exception-specific and global exceptions in a single place.
import java.util.Date;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
         ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
         return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> globleExcpetionHandler(Exception ex, WebRequest request) {
        ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

10. Running the Application

Finally, let’s define the application’s entry point. Like most Spring Boot applications, we can do this with a plain old main() method:
package net.guides.springboot2.springboot2jpacrudexample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class Application {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

11. Spring Rest Client

Once the above spring application is up and running, let's test all Spring rest APIs and logging results with the below Spring rest client:
package net.guides.springboot2.springboot2jpacrudexample;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import net.guides.springboot2.springboot2jpacrudexample.model.Employee;

public class SpringRestClient {

    private static final String GET_EMPLOYEES_ENDPOINT_URL = "http://localhost:8080/api/v1/employees";
    private static final String GET_EMPLOYEE_ENDPOINT_URL = "http://localhost:8080/api/v1/employees/{id}";
    private static final String CREATE_EMPLOYEE_ENDPOINT_URL = "http://localhost:8080/api/v1/employees";
    private static final String UPDATE_EMPLOYEE_ENDPOINT_URL = "http://localhost:8080/api/v1/employees/{id}";
    private static final String DELETE_EMPLOYEE_ENDPOINT_URL = "http://localhost:8080/api/v1/employees/{id}";
    private static RestTemplate restTemplate = new RestTemplate();

    public static void main(String[] args) {
        SpringRestClient springRestClient = new SpringRestClient();

        // Step1: first create a new employee
        springRestClient.createEmployee();

        // Step 2: get new created employee from step1
        springRestClient.getEmployeeById();

        // Step3: get all employees
        springRestClient.getEmployees();

        // Step4: Update employee with id = 1
        springRestClient.updateEmployee();

        // Step5: Delete employee with id = 1
        springRestClient.deleteEmployee();
    }

    private void getEmployees() {

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        HttpEntity < String > entity = new HttpEntity < String > ("parameters", headers);

        ResponseEntity < String > result = restTemplate.exchange(GET_EMPLOYEES_ENDPOINT_URL, HttpMethod.GET, entity,
            String.class);

        System.out.println(result);
    }

    private void getEmployeeById() {

        Map < String, String > params = new HashMap < String, String > ();
        params.put("id", "1");

        RestTemplate restTemplate = new RestTemplate();
        Employee result = restTemplate.getForObject(GET_EMPLOYEE_ENDPOINT_URL, Employee.class, params);

        System.out.println(result);
    }

    private void createEmployee() {

        Employee newEmployee = new Employee("admin", "admin", "admin@gmail.com");

        RestTemplate restTemplate = new RestTemplate();
        Employee result = restTemplate.postForObject(CREATE_EMPLOYEE_ENDPOINT_URL, newEmployee, Employee.class);

        System.out.println(result);
    }

    private void updateEmployee() {
        Map < String, String > params = new HashMap < String, String > ();
        params.put("id", "1");
        Employee updatedEmployee = new Employee("admin123", "admin123", "admin123@gmail.com");
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.put(UPDATE_EMPLOYEE_ENDPOINT_URL, updatedEmployee, params);
    }

    private void deleteEmployee() {
        Map < String, String > params = new HashMap < String, String > ();
        params.put("id", "1");
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.delete(DELETE_EMPLOYEE_ENDPOINT_URL, params);
    }
}
Output:
The logging statements are printed in the console, the above screenshot is for your reference.

12. Conclusion

We have learned how to integrate AspectJ annotations with the Spring AOP framework. We have developed a complete example to demonstrate the usage of AspectJ @Before, @After, @AfterReturning, @AfterThrowing, and @Around annotations.
The source code of this tutorial is hosted on my GitHub repository at https://github.com/RameshMF/spring-boot-tutorial

Comments