post-image

Spring Boot AOP Before Advice

AOP

1. Tổng quan

Ở bài viết trước chúng ta đã cùng tìm hiểu sơ qua về Spring AOP và các loại advice, bạn có thể xem lại bài viết ở đây.

Before advice được sử dụng trong lập trình hướng khía cạnh để đạt được kết quả xuyên suốt.

Nó là loại advice đảm bảo rằng một advice được chạy trước khi method thực hiện. Chúng ta có thể sử dụng annotation @Before để sử dụng before advice.

Ở bài viết này chúng ta hãy cùng tìm hiểu kỹ hơn loại advice này thông qua ví dụ dưới đây.

2. Ví dụ về Spring Boot before advice

  • Bước 1: Đầu tiên chúng ta khởi tạo dự án SpringBoot trong IntelIJ như sau: File -> New -> New Project -> Spring Initiaizr
  • Bước 2: Chọn Next và điền Group name, ở đây mình để com.example.
  • Bước 3: Điền thông tin về Artifact, ở đây mình để là demo.
  • Bước 4: Chọn type là Gradle và Java version 8 sau đó nhấn Next.
  • Bước 5: Sau đó chọn các thư viện như sau để tạo ứng dụng. Ở đây mình sử dụng Lombok để tự động tạo ra các getter setter. Các bạn có thể xem bài viết chi tiết hơn tại đây:
  • Bước 6: Đặt tên cho ứng dụng và nhấn finish để khởi tạo ứng dụng
  • Bước 7: Mở file build.gralde và thêm thư viện AOP vào trong dependency.
implementation 'org.springframework.boot:spring-boot-starter-aop'

build.gradle

plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    implementation 'org.springframework.boot:spring-boot-starter-aop'

}

test {
    useJUnitPlatform()
}
  • Bước 8: Mở file DemoApplication.java và thêm annotation @EnableAspectJAutoProxy vào trong class này.
@EnableAspectJAutoProxy(proxyTargetClass=true)

Nó cho phép hỗ trợ xử lý các thành phần được đánh dấu bằng annotation @Aspect của AspectJ. Nó được sử dụng với annotation @Configuration. Chúng ta có thể kiểm soát loại proxy bằng cách sử dụng thuộc tính proxyTargetClass. Giá trị mặc định của nó là false.

DemoApplication.java

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class DemoApplication {

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

}
  • Bước 9: Tạo model Employee nằm trong package com.example.demo.model với các thuộc tính và phương thức sau:
    • Các thuộc tính: employeeId, firstname, lastname.
    • Tạo các phương thức Getter Setter.
package com.example.demo.model;

import lombok.Data;

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

@Data
@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String employeeId;
    private String firstname;
    private String lastname;
}
  • Bước 10: Tạo controller EmployeeController nằm trong package com.example.demo.controller với các API thêm, sửa, xóa.
package com.example.demo.controller;

import com.example.demo.model.Employee;
import com.example.demo.service.IEmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController
@CrossOrigin("*")
@RequestMapping("/employees")
public class EmployeeController {
    @Autowired
    private IEmployeeService employeeService;

    @GetMapping
    public ResponseEntity<Iterable<Employee>> getAllEmployee() {
        return new ResponseEntity<>(employeeService.findAll(), HttpStatus.OK);
    }

    @PostMapping
    public ResponseEntity<Employee> createNewEmployee(@RequestBody Employee employee) {
        return new ResponseEntity<>(employeeService.save(employee), HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Employee> getEmployee(@PathVariable Long id) {
        Optional<Employee> employeeOptional = employeeService.findById(id);
        return employeeOptional.map(employee -> new ResponseEntity<>(employee, HttpStatus.OK))
                .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @PutMapping("/{id}")
    public ResponseEntity<Employee> updateEmployee(@PathVariable Long id, @RequestBody Employee employee) {
        Optional<Employee> employeeOptional = employeeService.findById(id);
        return employeeOptional.map(employee1 -> {
            employee.setId(employee1.getId());
            return new ResponseEntity<>(employeeService.save(employee), HttpStatus.OK);
        }).orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Employee> deleteEmployee(@PathVariable Long id) {
        Optional<Employee> employeeOptional = employeeService.findById(id);
        return employeeOptional.map(employee -> {
            employeeService.remove(id);
            return new ResponseEntity<>(employee, HttpStatus.OK);
        }).orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}
  • Bước 11: Tạo interface IGeneralService trong package com.example.demo.service và interface IEmployeeService kế thừa interface trên và một class EmployeeService implement interface IEmployeeService.

IGeneralService.java

package com.example.demo.service;

import java.util.Optional;

public interface IGeneralService<T> {
    Iterable<T> findAll();

    Optional<T> findById(Long id);

    T save(T t);

    void remove(Long id);
}

IEmployeeService.java

package com.example.demo.service;

import com.example.demo.model.Employee;

public interface IEmployeeService extends IGeneralService<Employee> {
}

EmployeeService.java

package com.example.demo.service;

import com.example.demo.model.Employee;
import com.example.demo.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class EmployeeService implements IEmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;

    @Override
    public Iterable<Employee> findAll() {
        return employeeRepository.findAll();
    }

    @Override
    public Optional<Employee> findById(Long id) {
        return employeeRepository.findById(id);
    }

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

    @Override
    public void remove(Long id) {
        employeeRepository.deleteById(id);
    }
}
  • Bước 12: Tạo interface EmployeeRepository kế thừa interface JpaRepository có sẵn của Spring Data Jpa trong package com.example.demo.repository.
package com.example.demo.repository;

import com.example.demo.model.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
  • Bước 13: Tạo một class có tên là EmployeeServiceAspect trong package com.example.demo.aspect.
package com.example.demo.aspect;

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

@Aspect
@Component
public class EmployeeServiceAspect {
    @Before(value = "execution(* com.example.demo.service.EmployeeService.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method:" + joinPoint.getSignature());
        System.out.println("Creating Employee success");
    }
}
  • Bước 14: Cấu hình file application.propperties:
#Database
spring.datasource.url=${JDBC_DATABASE_URL:}
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#Hibernate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.id.new_generator_mappings = true
spring.jpa.properties.hibernate.show.sql = true
spring.jpa.properties.hibernate.format_sql = true
spring.jpa.generate-ddl=true
spring.jpa.properties.hibernate.hb2dll.auto = update
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

Ở đây mình sử dụng đến file *.env để cấu hình đường dẫn đến schema. Bạn có thể xem hướng dẫn cách sử dụng env trong Spring Boot tại đây.

local.env

JDBC_DATABASE_URL=jdbc:mysql://localhost:3306/demo_aop
  • Bước 15: Khởi chạy ứng dụng và sử dụng postman để chạy đường dẫn sau và chọn phương thức POST: http://localhost:8080/employees.

Ở trên mình truyền dữ liệu dạng Json có các thông tin employeeId = 001, firstname = Quan, lastname = Nguyen.

  • Bước 16: Mở màn hình console của ứng dụng và xem kết quả

3. Kết luận

Ở bài viết này chúng ta đã học được cách làm việc với before advice. Trong bài viết tiếp theo chúng ta sẽ tìm hiểu về after advice và cách triển khai nó trong ứng dụng của mình.

Nguồn tham khảo: https://www.javatpoint.com/spring-boot-aop-before-advice

Leave a Reply

Your email address will not be published.