[Bài đọc] Những mối quan tâm xuyên suốt và Lập trình Hướng Khía cạnh

9.AOP

Cross Cutting Concerns (những mối quan tâm xuyên suốt) có thể coi là vấn đề mà từ đó hình thành kỹ thuật Lập trình Hướng Khía cạnh. Mối quan tâm xuyên suốt là những khía cạnh (cả mối quan tâm và khía cạnh ở đây có thể hiểu là nghiệp vụ) của một chương trình mà ảnh hưởng đến các mối quan tâm khác khác. Những mối quan tâm này thường không thể bị tách rời khỏi phần còn lại của hệ thống, và có thể gây ra lặp mã, rối mã, hay cả hai.

Lập trình khía cạnh giúp mô-đun hóa các mối quan tâm xuyên suốt, nói cách khác, các mối quan tâm xuyên suốt sẽ được định nghĩa thành các hàm mà có thể ảnh hưởng tới nhiều vị trí trong chương trình. Bảo mật là một ví dụ điển hình – rất nhiều vị trí trong chương trình đều có các luật bảo mật đặc thù. Hình trên lấy ví dụ một số mối quan tẩm xuyên suốt qua nhiều tầng của chương trình. Còn hình dưới đây biểu diễn một số mối quan tâm xuyên suốt tồn tại trên nhiều khía cạnh nghiệp vụ khác nhau.

Mặc dù tồn tại một số phương pháp khác để khử các vấn đề xoay quanh mã lặp và rối khi gặp các mối quan tâm xuyên suốt, như sử dụng kế thừa hay ủy quyền (delegate), nhưng lập trình khía cạnh tỏ ra trong sáng và hoàn thiện hơn. Với AOP, ta giữ nguyên được khả năng định nghĩa các hàm dùng chung tại một nơi duy nhất, nhưng có thể áp dụng các hàm đó tại nhiều nơi khác nhau mà không phải chỉnh sửa các class liên quan. Trong ngữ cảnh của AOP, các mối quan tâm xuyên suốt được gói vào trong các class được gọi là Khía cạnh (aspect). Và nhờ đó các hàm service chỉ phải chứa các mã mà quan tâm trực tiếp đến nghiệp vụ.

Một số thuật ngữ quan trọng trong AOP

Advice (lời khuyên), Pointcut (điểm cắt), Join point (điểm nối) là những thuật ngữ quan trọng trong AOP.

Join points

Điểm nối, là những vị trí xác định trong luồng thực thi của chương trình mà tại đó một khía cạnh sẽ bắt đầu tham gia vào. Điểm gia nhập có thể là một lời gọi phương thức, một lệnh tung expection, hay một lệnh chỉnh sửa thuộc tính …

Advice

Là hành động thực sự mà aspect sẽ làm, trước, hoặc sau – khi thực thi phương thức. Đây là mô tả quan trọng nhất của một mối quan tâm – nó làm gì, và khi nào.

Weaving

Dệt – là hành động ghép aspect và đối tượng mục tiêu áp dụng của nó vào với nhau thành một object mới (và sau đó chương trình thực thi theo object mới đó). Aspect có thể được dệt tại một vài thời điểm:

  • Khi compile: aspect được dệt vào mục tiêu tại thời điểm compile. Điều này cần có compiler đặc thù. Framework AOC AspectJ là một framework điển hình.
  • Khi nạp class mục tiêu vào JVM: điều này yêu cầu một class loader đặc thù (xem lại về cách hoạt động của JVM), framework AspectJ cung cấp một classloader điển hình.
  • Tại thời điểm thực thi: dệt diễn ra khi chương trình đang chạy, đây là cách được Spring sử dụng.

Pointcut

Điểm cắt là những biểu thức đi theo join points để xác định xem một hay một tập hợp các advice sẽ được khởi động hay không.

Hỗ trợ từ Spring

Spring là một trong số những AOP framework. Một số framework chỉ cho phép đặt joint point vào các lời gọi hàm, hay lệnh chỉnh sửa thuộc tính, … Hay khác nhau ở  cách weaving. Tuy vậy, điểm chung là chúng đều có khả năng đặt ra một điểm cắt mà tại đó cho phép dệt một aspect vào.

Spring hỗ trợ lập trình viên triển khai AOP theo bốn cách:

  • Cổ điển
  • Thuần POJO
  • Sử dụng bộ annotation của framework AspectJ
  • Sử dụng trực tiếp các aspect của AspectJ bằng tiêm phụ thuộc

Cả 3 cách đầu tiên đều là biến thể của AOP nguyên bản của Spring. Hiện tại cách thứ ba là phương án cho mã AOP trong sáng nhất. Tuy nhiên, AOP nguyên bản của Spring chỉ hỗ trợ joint point tại lời gọi phương thức, nếu muốn đặt joint point tại những vị trí khác thì bắt buộc phải dùng tới các aspect từ AspectJ.

Một trong những dấu hiệu của việc này là nhu cầu bổ sung thư viện spring-aspects vào dự án:

compile group: 'org.springframework', name: 'spring-aspects', version: '4.3.25.RELEASE'

Các loại Advice

Spring hỗ trợ 5 loại advice sau để đưa một aspect vào tiến trình thực thi

  • before: được thực thi trước khi method được thực thi
  • after: được thực thi sau khi method được thực thi
  • after-returning: được thực thi sau khi và chỉ nếu khi method được thực thi thành công
  • after-throwing: được thực thi sau khi và chỉ nếu khi method được thực thi không thành công, nghĩa là có ngoại lệ được throw
  • around: được thực thi cả trước và sau khi method được thực thi

Một ví dụ về advice là như sau:

@Aspect
public class Logging {
@AfterThrowing(pointcut = "execution(* cg.wbd.grandemonstration.controller.CustomerController.showList(..))", throwing = "e")
public void log(Exception e) {
System.out.println("--------------------------- co loi xay ra: " + e.getMessage());
}
}

Trong ví dụ trên, advice “log” của aspect Logging là một after-throwing advice, nó được thực thi mỗi khi có expection từ phương thức showList của class CustomerController được thực thi dù với bất kỳ bộ tham số nào (dấu ..).

Proxy object

Trong Spring AOP, các aspect được dệt vào các bean do Spring quản lý tại runtime, bằng cách bao gói các bean đó trong proxy class. Bằng cách đó, lời gọi phương thức của target sẽ biến thành lời gọi phương thức của proxy, và proxy có cơ hội thực thi advice trước khi gọi phương thức mục tiêu

Định nghĩa một point cut

Trong ví dụ về logging ở trên đã có một point cut. Point cut mô tả bao giờ thì advice được áp dụng. Trong ngôn ngữ của AspectJ có những từ khóa đặc biệt để mô tả về point cut. Bạn có thể tham khảo tại đường dẫn: https://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/html/aop.html#aop-pointcuts

Leave a Reply

Your email address will not be published.