Tìm hiểu AOP với AspectJ
1. AspectJ là gì? AspectJ là sự mở rộng theo mô hình AOP của ngôn ngữ Java, với sự mở rộng này mã chương trình viết bằng Java sẽ tương thích với chương trình viết bằng AspectJ. AspectJ bao gồm hai phần: đặc tả ngôn ngữ và phần thực thi. Phần đặc tả ngôn ngữ sẽ chỉ ra cách viết code, ...
1. AspectJ là gì?
-
AspectJ là sự mở rộng theo mô hình AOP của ngôn ngữ Java, với sự mở rộng này mã chương trình viết bằng Java sẽ tương thích với chương trình viết bằng AspectJ.
-
AspectJ bao gồm hai phần: đặc tả ngôn ngữ và phần thực thi. Phần đặc tả ngôn ngữ sẽ chỉ ra cách viết code, với AspectJ các concern cơ bản được viết bằng Java, các concern hệ thống được thực hiện bởi AspectJ. AspectJ đã được plugin vào công cụ phát triển Eclipse (eclipse.org) và được đánh giá là sản phẩm tốt nhất hiện nay về AOP
2. Một số khái niệm
Như đã biết mục đích của AOP là module hoá các concern đan xen nhau. AspectJ định nghĩa 2 loại concern:
- Static crosscutting: Là sự gắn kết thay đổi trong một cấu trúc tĩnh (class, interface, aspect). Nghĩa là khi chúng ta thực hiện một thay đổi đến các cấu trúc dữ liệu tĩnh để đáp ứng các yêu cầu của bài toán.
- Dynamic crosscutting: là sự gắn kết các hành vi mới vào chương trình, hầu hết các concern đan xen trong AspectJ là concern đan xen động.
AspectJ sử dụng dự mở rộng của Java để chỉ ra các luật gắn kết cho Static crosscutting và dynamic crosscutting.
2.1. Join point
-
Join point là một khái niệm cở bản của AspectJ, Join point có thể là bất kỳ điểm nào có thể xác định được khi thực hiện chương trình. Có thể là lời gọi đến một phương thức hoặc một lệnh gán cho một biến của đối tượng. Trong AspectJ mọi thứ đều xoay quanh join point.
-
Join point được phân loại như sau:
- join point tại các phương thức
- join point tại hàm dựng (contructor)
- join point tại điểm truy cập các thuộc tính
- join point tại điểm điều khiển ngoại lệ: Được biểu diễn trong khối điều khiển ngoại lệ
- • join point tại các advice
2.2. Pointcut
-
Pointcut là một cấu trúc chương trình mà nó chọn các join point và ngữ cảnh tại các joint point đó. Ví dụ một pointcut có thể chọn một join point là một lời gọi đến một phương thức và lấy thông tin ngữ cảnh của phương thức đó như đối tượng chứa phương thức, các đối số của phương thức đó.
-
Cú pháp của pointcut được khai báo như sau
[access specifier] pointcut pointcut-name([args]) : pointcut-definition
-
Ví dụ
execution(void Account.creadit(float))
- Bảng ánh xạ giữa các join point được chọn cho các point cut
2.3. Advice
-
Là mã được thực hiện tại một join point mà đã được chọn bởi pointcut. Advice tương tự cấu trúc của hàm cung cấp các thức các hành động đan xen tại các join point mà nó được chọn bởi point cut. Pointcut và advice sẽ hình thành nên các luật đan kết các quan hệ đan xen.
-
Advice được chia thành 3 loại sau
- before: Được thực hiện trước join point
- after: Được thực hiện sau join point
- around: Bao quanh sự thực hiện join point, advice này có thể thực hiện vòng, thực hiện tiếp của mã nguồn ban đầu hoặc thực hiện thay đổi ngữ cảnh (tham số của hàm, …)
-
Ví dụ: Giả sử ta có pointcut được khai báo như sau
pointcut connectionOperation(Connection connection) : call(* Connection.*(..) throws SQLException)&& target(connection);
Ta có thể xây dựng các advice như sau:
before(Connection connection):connectionOperation (connection) { System.out.println("Performing operation on " + connection); } Object around(Connection connection) throws SQLException : connectionOperation (connection) { System.out.println("Operation " + thisJoinPoint + " on " + connection+ " started at " + System.currentTimeMillis()); proceed(connection); System.out.println("Operation " + thisJoinPoint + " on " + connection + " completed at " + System.currentTimeMillis()); }
Nếu trong around advice muốn thực hiện thao tác tại join point, phải sử dụng từ khoá proceed().
2.4. Introduction
-
Introduction là một lệnh chỉ ra sự thay đổi đến một class, interface, aspect. Nó tạo ra sự thay đổi tĩnh đến các module mà không trực tiếp ảnh hưởng đến các hành vi của module đó. Ví dụ có thể thêm một phương thức hoặc một trường vào lớp nào đó hoặc sửa đổi cấu trúc thừa kế của một đối tượng.
-
Ví dụ khai báo sau sẽ sửa đổi cấu trúc thừa kế của đối tượng Account.
declare parents: Account implements BankingEntity;
-
Introduction là khái niệm sinh ra để can thiệp vào các cấu trúc tĩnh, trong AOP nó được dùng để xử lý các quan hệ đan xen tĩnh (static crosscutting). Chúng ta sẽ đề cập đến vấn đề này trong phần static crosscutting.
2.5. Aspect
-
Aspect là phần tử tập trung của AspectJ, giống như class trong Java. Aspect chứa mã thể hiện các luật đan kết cho concern. Join point, pointcut, advice, introduction được kết hợp trong aspect.
-
Aspect được khai báo theo mẫu sau
[access specification] aspect <AspectName> [extends class-or-aspect-name] [implements interface-list] [<association-specifier>(Pointcut)] { ... aspect body }
- Ví dụ sau khai báo một aspect:
public aspect ExampleAspect { before() : execution(void Account.credit(float)) { System.out.println("About to perform credit operation"); } declare parents: Account implements BankingEntity; declare warning : call(void Persistence.save(Object)) : "Consider using Persistence.saveOptimized()"; }
-
Một số tính chất của khái niệm aspect tương tự khái niệm class
- Aspect có thể chứa các thuộc tính và phương thức
- Aspect chứa các thuộc tính truy cập: private, public, protected
- Aspect có thể khai báo như một aspect trừu tượng
public abstract aspect AbstractLogging { public abstract pointcut logPoints(); public abstract Logger getLogger(); before() : logPoints() { getLogger().log(Level.INFO, "Before: " + thisJoinPoint); } }
- Aspect có thể thừa kế class, abstract aspect và thi công các interface
public aspect BankLogging extends AbstractLogging { public pointcut logPoints() : call(* banking..*(..)); public Logger getLogger() { return Logger.getLogger("banking"); } }
- Ngoài các tính chất tương tự class như trên, aspect cũng có một số đặc điểm khác so với class như sau:
- Aspect không thể khởi tạo trực tiếp
- Aspect không thể thừa kế từ một aspect khác (không phải trừu tượng)
- Aspect có thể được đánh dấu như quyền
3. Giải quyết bài toán với AOP
-
Trong phần này chúng ta sẽ xem xét một số bài toán và một số giải pháp thực tiễn để giúp một ứng dụng triển khai được trên AOP. Việc áp dụng một công nghệ mới để giải quyết bài toán không bao giờ dễ dàng, đặc biệt khi bạn còn chưa nhìn thấy một hệ thống nào triển khai thành công với công nghệ mới này. Chúng ta sẽ tìm hiểu cách sử dụng mô hình hướng aspect như thế nào để giải quyết bài toán và các giải pháp về thiết kế.
-
Một khi thực sự chắc chắn muốn sử dụng AOP trong hệ thống phần mềm nào đó, ta cần xác định tính thích hợp cho mỗi vấn đề khác nhau. Cần phải xem xét khả năng tối thiểu các rủi ro của hệ thống. Chẳng hạn chúng ta có thể áp dụng AOP cho các module con, sau khi chứng minh được khả năng của AOP, chúng ta sẽ tiếp tục triển khai trên các module tiếp theo.
-
Mỗi pha trong quá trình phát triển phần mềm: thiết kế, thi công, test và bảo trì đều nhấn mạnh tới một số hành động.
3.1. Sử dụng AOP trong bước thiết kê
-
Nếu sử dụng AOP trong bước thiết kế chúng ta sẽ có được nhiều sự thuận lợi mà AOP đem lại. Từ quan điểm về kiến trúc, sự thuận lợi chính là giúp chúng ta vượt qua sự bế tắc của các kiến trúc hiện tại.
-
Sau đây là một số bước điển hình sử dụng AOP trong pha thiết kế:
- Nhận biết các concern đan xen: Bước này là một phần trong việc ánh xạ các yêu cầu người dùng tới các module. Một quy tắc là xem xét các concern được mô tả với các tính tù hoặc trạng từ bắt đầu với từ “mọi”, ví dụ như mọi ngày, mọi nơi…Nhận biết các concern này ban đầu sẽ giúp chúng ta tránh khỏi việc module hoá các concern đan xen theo phương pháp truyền thống.
- Thiết kế các concern lõi trước: Áp dụng các quy tắc và phương pháp truyền thống để thiết kế các concern lõi. Công việc này càng làm tốt thì việc áp dụng các concern đan xen sau này càng dễ.
- Thiết kế các concern đan xen: Xác định các concern đan xen cần thiết, dễ thấy. Lên một bộ khung cho các concern bạn cần và cũng có thể cả các concern bạn chưa cần ngay lập tức
3.2. Sử dụng AOP trong bước thi công
-
Khi sử dụng AOP trong bước thi công bạn nên nhấn mạnh vào trên một vài thực tiễn có tính chất chung. Cũng như cần theo một số chỉ dẫn để việc thi công các concern lõi và concern đan xen dẽ nhất có thể. Cũng có một số phương pháp refactoring theo mô hình AOP bạn có thể sử dụng.
-
Thực thi các concern lõi:
- Viết các concern lõi theo mô hình refactoring tốt nhất.
- Sử dụng cách đặt tên nhất quán xuyên suốt ứng dụng
- Tách biệt các concern đan xen từ các module trong bước đầu tiên.
- Xem xét bất kỳ sự rải rác và chồng chéo mã chương trình
-
Thực thi các concern đan xen
- Xác định các join point: Bước này cần xác định các vị trí trong mã chương trình cần cho các quan hệ đan xen. Tiếp theo cần quyết định cách tốt nhất để thể hiện các pointcut mà chúng sẽ chọn các join point.
- Lựa chọn các kỹ thuật sử dụng ở lớp dưới
- Thiết kế các aspect
-
Thực hiện refactoring các aspect
3.3. Sử dụng AOP trong bước kiểm tra
-
AspectJ có thể trợ giúp nhiều nhiệm vụ trong bước kiếm tra, Sau đây là một kịch bản điển hình mà chúng ta có thể bắt đầu thực hiện với AspectJ .
-
Tạo các test case: Do AspectJ có khả năng sửa đổi các hành vi mà không cần sự thay đổi thực sự nên AspectJ có thể trợ giúp để viết các chương trình kiểm tra.
-
Thực hiện kiểm tra hiệu năng hệ thống: Rất nhiều vấn đề chỉ được phát hiện ra vào thời điểm triển khai hệ thống.AspectJ có thể bất chế độ theo dõi hiệu năng các aspect, do đó chúng ta có thể xác định được kết quả gần với các hệ thống thực, và chúng ta có thể quyết định sử dụng aspect hay không trên hệ thống triển khai để tránh tràn bộ nhớ.
-
Báo cáo lỗi: Trong quá trình kiểm tra, khi chúng ta phát hiện ra các lỗi thì có thể sử dụng aspect để chỉ ra các ngữ cảnh trong ứng dụng chứ không phải chỉ là ngăn xếp của ngoại lệ được ném ra.
3.4. Sử dụng AOP trong giai đoạn bảo trì
-
Giai đoạn bảo trì hệ thống bao gồm hai thao tác chính sau
- Thêm mới tính năng cho các yêu cầu mới
- Sửa các lỗi được tìm thấy
-
AspectJ có thể điều khiển 2 bước sau trong giai đoạn bảo trì
- Tạo một bức tường an toàn: Thêm các tính năng mới mà không làm đổ vỡ hệ thống, Các chế độ tăng cường của aspect bảo đảm rằng các tính năng mới không ảnh hưởng đến hệ thống cũng như tạo ra các lỗi mới
- Thực thi các tính năng mới: AspectJ có thể thêm các quan hệ đan xen mới mà không thay đổi trực tiếp trên mã nguồn gốc