Bài 12: Pointcut nâng cao với Annotation & Regex

hoangkimhoangkim
5 min read

Mục tiêu bài học

  • Nắm được cách sử dụng annotation-based pointcut phức tạp: Hiểu cách khai báo và sử dụng các pointcut dựa trên annotation để áp dụng logic AOP vào các method cụ thể.

  • Kết hợp regex (naming convention) và annotation: Học cách sử dụng biểu thức chính quy cùng với annotation để tạo các pointcut mạnh mẽ và linh hoạt hơn.

  • Xây dựng logic chặt chẽ hơn, tránh match nhầm: Phát triển các pointcut chính xác, hạn chế việc áp dụng advice vào các method không mong muốn, đảm bảo hiệu quả và tính nhất quán trong ứng dụng.


Nội dung chính

1. Annotation-based Pointcut

  • @annotation(YourAnnotation)@target(YourTypeAnnotation):

    • @annotation(YourAnnotation): Bắt các method được đánh dấu bằng annotation cụ thể.

    • @target(YourTypeAnnotation): Bắt các bean hoặc class được đánh dấu bằng annotation cụ thể.

  • Mức class, mức method:

    • Mức method: Áp dụng advice cho các method cụ thể được đánh dấu bằng annotation.

    • Mức class: Áp dụng advice cho tất cả các method trong class được đánh dấu bằng annotation.

Ví dụ:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecution {
    String value() default "";
}

2. Regex-based Pointcut

  • execution( ServiceImpl.find(..))*:

    • Sử dụng biểu thức execution để bắt các method bắt đầu bằng find trong các class kết thúc bằng ServiceImpl.
  • Sử dụng wildcard *, ..:

    • *: Đại diện cho bất kỳ tên gì.

    • ..: Đại diện cho bất kỳ số lượng tham số nào.

Ví dụ:

@Pointcut("execution(* com.example.service..*ServiceImpl.find*(..))")
public void findMethods() {}

3. Kết hợp

  • (execution( find(..)) && @annotation(TrackTime))**:

    • Áp dụng advice chỉ cho các method bắt đầu bằng find và được đánh dấu bằng annotation @TrackTime.
  • Lợi ích khi handle logic phức tạp:

    • Chỉ áp dụng advice vào các method cần thiết, đảm bảo không ảnh hưởng đến các method khác.

    • Tăng tính linh hoạt và kiểm soát chính xác trong việc áp dụng AOP.

Ví dụ:

@Aspect
@Component
public class TrackingAspect {

    @Pointcut("(execution(* find*(..)) && @annotation(com.example.annotations.TrackTime))")
    public void trackedFindMethods() {}

    @Around("trackedFindMethods()")
    public Object trackExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object retVal = pjp.proceed();
        long duration = System.currentTimeMillis() - start;
        System.out.println("Execution time of " + pjp.getSignature() + " : " + duration + " ms");
        return retVal;
    }
}

4. Ví dụ code

  1. Tự động validate input (annotation @ValidateInput):

     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface ValidateInput {
         // Có thể thêm các tham số tùy chọn
     }
    
     @Aspect
     @Component
     public class ValidationAspect {
    
         @Pointcut("@annotation(com.example.annotations.ValidateInput)")
         public void validateMethods() {}
    
         @Before("validateMethods()")
         public void validate(JoinPoint joinPoint) {
             Object[] args = joinPoint.getArgs();
             for (Object arg : args) {
                 if (arg == null) {
                     throw new IllegalArgumentException("Argument cannot be null");
                 }
                 // Thêm các logic validate khác nếu cần
             }
         }
     }
    
     @Service
     public class UserService {
    
         @ValidateInput
         public void createUser(User user) {
             // Logic tạo user
         }
     }
    
  2. Regex “save” method -> trigger auditing*:

     @Aspect
     @Component
     public class AuditingAspect {
    
         @Pointcut("execution(* save*(..))")
         public void saveMethods() {}
    
         @After("saveMethods()")
         public void auditSave(JoinPoint joinPoint) {
             String methodName = joinPoint.getSignature().getName();
             System.out.println("Audit: Method " + methodName + " was called.");
             // Thêm logic auditing, ví dụ: lưu log vào database
         }
     }
    

5. Best Practices

  • Annotation-based thường dễ đọc, rõ ràng ý đồ:

    • Việc sử dụng annotation giúp code trở nên rõ ràng hơn, dễ hiểu cho các developer khác.

    • Dễ dàng quản lý và cập nhật khi cần thay đổi logic AOP.

  • Regex phù hợp khi pattern code nhất quán (naming convention):

    • Khi các method trong project tuân theo naming convention chặt chẽ, regex-based pointcut giúp dễ dàng áp dụng AOP mà không cần phải thêm annotation vào từng method.

    • Giảm thiểu việc phải thay đổi code gốc khi muốn áp dụng hoặc loại bỏ AOP.

6. Debug Pointcut

  • Kiểm tra “match” class/method nào, in log:

    • Thêm log vào trong aspect để kiểm tra xem pointcut có match đúng các method cần thiết không.
  • Luôn review cẩn thận để tránh match vô tình:

    • Đảm bảo rằng biểu thức pointcut được viết chính xác để không áp dụng AOP vào các method không mong muốn, tránh gây ảnh hưởng tiêu cực đến hiệu suất và logic nghiệp vụ.

Ví dụ:

@Aspect
@Component
public class DebuggingAspect {

    @Pointcut("(execution(* find*(..)) && @annotation(com.example.annotations.TrackTime))")
    public void trackedFindMethods() {}

    @Around("trackedFindMethods()")
    public Object debugTrackedMethods(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("Attempting to match method: " + pjp.getSignature());
        Object retVal = pjp.proceed();
        return retVal;
    }
}

Tóm tắt

  • Hiểu cách kết hợp annotation và regex trong pointcut nâng cao: Sử dụng các kỹ thuật annotation-based và regex-based pointcut để áp dụng AOP một cách linh hoạt và chính xác hơn.

  • Tăng cường kiểm soát: Giảm nguy cơ áp dụng AOP vào các method không mong muốn, đảm bảo hiệu quả và tính nhất quán trong ứng dụng.

  • Sang Bài 13: Học cách xây dựng annotation và aspect tuỳ biến để phục vụ các nhu cầu cụ thể hơn trong dự án.


Câu hỏi thảo luận

  1. Khi nào regex-based “lợi hại” hơn annotation-based?

    • Lợi hại của regex-based:

      • Lợi: Dễ dàng áp dụng AOP vào nhiều method theo pattern nhất định mà không cần thêm annotation vào từng method. Phù hợp với các dự án có naming convention chặt chẽ.

      • Hại: Có thể áp dụng AOP vào các method không mong muốn nếu biểu thức regex không chính xác. Khó kiểm soát và hiểu rõ các method bị ảnh hưởng nếu không quản lý tốt.

  2. Ví dụ thực tế khác của @annotation-based pointcut?

    • @Transactional: Spring sử dụng annotation này để áp dụng quản lý transaction, tương tự như cách sử dụng annotation-based pointcut để áp dụng logic AOP.

    • @Retryable: Thêm vào các method để tự động retry khi gặp exception, sử dụng AOP để quản lý số lần retry.

    • @Cacheable: Áp dụng caching cho các method, sử dụng AOP để kiểm tra và lưu cache mà không cần thay đổi code gốc.


0
Subscribe to my newsletter

Read articles from hoangkim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

hoangkim
hoangkim