Các công cụ AOP

Một phần của tài liệu Nghiên cứu về kiểm chứng bất biến của đối tượng sử dụng lập trình hướng khía cạnh (Trang 26)

Mô hình cơ sở của AOP là mô hình join point. Tất cả các công cụ dùng mô hình này để cung cấp phương tiện nhận dạng nơi mà các mối quan tâm cắt ngang được áp dụng. Tuy nhiên, các công cụ khác nhau thực hiện các mô hình aspect theo cách của nó và đưa ra các ngữ nghĩa và kỹ thuật đan kết các aspect mới.

Ví dụ, trong JBoss AOP [27], các advice được thực thi thông qua bộ chặn (interceptor) sử dụng Java reflection và các pointcut được khai báo trong một tập XML mô tả nơi để đan kết trong một advice một cách tự động tại thời điểm thực thi. Trong AspectJ, cả advice và pointcut được khai báo trong một lớp aspect và được đan kết tĩnh.

Tính đa dạng này trong các công cụ AOP là khó giải quyết đối với phát triển phần mềm sử dụng aspect vì sự khác nhau về ngữ nghĩa của các mô hình AOP và các cách khác nhau một aspect được đan kết với các lớp khác. Khó có thể phát triển lại một cách đơn giản một aspect đang tồn tại để nó có thể đan kết với các aspect được phát triển bởi mô hình AOP khác.

Để giải quyết vấn đề này, AspectWerkz [28] đã đề xuất một mô hình kiến trúc mở cho một bộ chứa aspect (aspect container) đối với các công cụ Java dựa trên AOP. Ý tướng cơ bản là để cung cấp một bộ đan và một bộ chứa (container) có thể đan, triển khai và chạy bất kỳ aspect không quan tâm đến việc nó được cài đặt và khai báo như thế nào. Nó có trách nhiệm giải quyết sự khác nhau giữa các mô hình aspect. Một bể chứa như vậy có thể cho phép các aspect được phát triển bởi các mô hình AOP và công cụ khác nhau cùng tồn tại trong một môi trường thực thi. Hiện tại, AspectWerkz đã thực hiện một mô hình mở rộng cho các aspect thực hiện các giao tiếp AOP Alliance, Spring AOP [8, 24] và AspectJ [11, 13, 26].

Thiết kế và thực thi hướng khía cạnh đòi hỏi sự hỗ trợ của các công cụ AOP hiệu quả. Với các công cụ này, các nghiên cứu và phát triển đang tiến hành đang cố gắng cung cấp các giải pháp tốt hơn trong một số phạm vi như bảo trì, hiệu suất, tích hợp, …

Bảo trì: việc thiết kế các hệ thống hướng khía cạnh chất lượng có nghĩa là chú tâm đến việc định nghĩa các pointcut lan tỏa và việc sử dụng quyền thừa kế các aspect một cách hợp lý. Các pointcut bắt giữ nhiều joint point hơn mong đợi hay bỏ xót các joint point mong muốn có thể gây đổ vỡ hệ thống khi được cải tiến. Do vậy một công cụ gỡ lỗi hiệu quả là cần thiết để dò tìm sự thực thi các joint point và các point cut lỗi.

Hiệu suất: việc sử dụng AOP tăng thêm các chi phí về hiệu suất trong các ứng dụng, cả trong quá trình đan kết và tiềm tàng lúc thực thi. AOP cần giảm thời đan kết và biên dịch đông thời tăng hiệu suất thực thi.

Tích hợp: việc sử dụng lại các aspect không được khám phá đầy đủ sẽ khiến các các nhà phát triển các thư viện aspect từ các aspect bị lỗi. Mỗi công cụ AOP chỉ cung cấp sự thực hiện các aspect cho mô hình của công cụ đó. Một aspect được thực hiện bằng một mô hình AOP cụ thể không thể được đan kết một cách dễ dàng bằng một mô hình AOP khác. Đây là chướng ngại vật lớn trong việc phát triển phần mềm hướng khía cạnh.

1.4. AspectJ

Lập trình hướng khía cạnh là cách mô-đun hóa các mối quan tâm cắt ngang giống như lập trình hướng đối tượng là cách để mô-đun hóa các mối quan tâm thông thường. AspectJ [11, 13, 26] là một thực thi lập trình hướng đối tượng cho ngôn ngữ lập trình Java. AspectJ mở rộng một số khái niệm của ngôn ngữ Java. Trình biên dịch AspectJ sẽ biên dịch các từ khóa cụ thể của AspectJ thành byte-code và tạo điều kiện thuận lợi cho việc đan kết các byte-code vào trong một tệp lớp (.class). Một aspect sẽ được biên dịch thành mã aspect được chứa trong một lớp và các cấu trúc cụ thể khác của AspectJ được chuyển đổi thành Java chuẩn. Trình biên dịch AspectJ đan kết aspect dưới dạng byte-code vào trong byte-code của ứng dụng chính và tạo ra các tệp lớp Java phù hợp có thể thực thi được bởi máy ảo Java.

Để thực hiện AOP bằng AspectJ nhà phát triển tách biệt hai phần: Phần đặc tả ngôn ngữ và phần thực thi ngôn ngữ. Phần đặc tả ngôn ngữ định nghĩa ngôn ngữ viết mã chương trình. Với AspectJ, các mối quan tâm nghiệp vụ chính được cài đặt trên ngôn ngữ lập trình Java còn các mối quan tâm cắt ngang và các quy tắc đan được cài đặt trên các mở rộng của AspectJ. Phần thực thi ngôn ngữ cung cấp các công cụ biên dịch, gỡ lỗi và tích hợp với các môi trường phát triển tích hợp khác phổ biến [11, 13].

Trong AOP, công việc kết hợp (đan - weaving) mối quan tâm cắt ngang với các mối quan tâm nghiệp vụ chính được thực hiện dựa trên các quy tắc đan. Các quy tắc đan chỉ ra hành động nào được thực hiện khi một số điểm nào đó trong quá trình thực thi chương trình xảy ra. Với AspectJ khi thực thi AOP, trình biên dịch AspectJ sử dụng các mô-đun cài đặt các mối quan tâm cắt ngang chứa các quy tắc đan (các aspect) để chèn thêm hành vi mới cho các mô-đun cài đặt các mối quan tâm nghiệp vụ chính mà không cần thay đổi mã nguồn của các mô-đun nghiệp vụ chính. Việc đan mã nguồn nghiệp vụ chính với các aspect chỉ thực hiện trong byte-code mà trình biên dịch sinh ra.

1.4.1. Thực thi cắt ngang

Như ở phần trên đã trình bày về các loại đan kết. AspectJ hỗ trợ tất cả các loại đan kết bao gồm đan kết lúc biên dịch (CTW), đan kết lúc nạp chương trình (LTW) và đan kết lúc chương trình đang thực thi (RTW). Việc đan kết này còn được gọi là thực thi cắt ngang (crosscutting). Việc đan kết phải tuân theo các quy tắc nhất định gọi là các quy tắc đan. Các quy tắc đan này cắt ngang nhiều mô-đun một cách có hệ thống

nhằm mô-đun hóa các mối quan tâm cắt ngang. AspectJ định nghĩa ra hai loại thực thi cắt ngang, đó là thực thi cắt ngang tĩnh (static crosscutting) và thực thi cắt ngang động (dynamic crosscutting).

Thực thi cắt ngang tĩnh: là việc đan hay chèn thêm các sửa đổi vào trong một lớp, giao diện hay aspect của hệ thống hoặc thay đổi tính thừa kế hay thực thi giao diện của các lớp trong cây thừa kế. Thực thi cắt ngang tĩnh còn được sử dụng để khai báo các cảnh báo và lỗi tại thời điểm biên dịch. Thông thường thực thi cắt ngang tĩnh là để chuẩn bị hay hỗ trợ cho thực thi cắt ngang động.

Thực thi cắt ngang động: là việc đan hành vi mới vào quá trình thực thi của chương trình. Việc đan hành vi mới theo cách cắt ngang nhiều môđun vì thế làm thay đổi hành vi của hệ thống. Ví dụ muốn chèn thêm một hành động lưu vết sau khi lưu dữ liệu vào cơ sở dữ liệu ta chỉ cần chỉ ra điểm đan là điểm sau thao tác lưu dữ liệu kết thúc.

AspectJ sử dụng các cú pháp mở rộng cho ngôn ngữ lập trình Java để chỉ ra các quy tắc đan cho việc thực thi cắt ngang động và thực thi cắt ngang tĩnh. Để thực hiện các quy tắc đan một cách tự động, AspectJ sử dụng các cấu trúc và bộ mô tả sau để mô tả việc cài đặt các mối quan tâm cắt ngang hệ thống như: Join point, point cut, advice, introduction, chỉ thị biên dịch và aspect.

1.4.2. Joint Point

Bộ biên dịch AspectJ cần tìm các vị trí trong mã nguồn hoặc byte-code nơi mà các hành vi mới được đan vào được thực thi. Những điểm có thể xác định đó được gọi là các joint point. Đó có thể là một lời gọi đến một phương thức hoặc lệnh đọc hay gán đối với một thành viên của một đối tượng.

Một số join point chính trong AspectJ như sau:

Joint point triệu gọi phương thức (Method call join point): xảy ra khi bất kỳ lời gọi phương thức nào được thiết lập bởi một đối tượng hay một phương thức tĩnh. Joint point này nằm trong một đối tượng hay ứng dụng triệu gọi phương thức, thường là nằm trong các phương thức triệu gọi phương thức đang xét. Chúng ta xem xét ví dụ sau:

ATMcard c=new ATMcard (0, 3000);

c.withdrawMoney(4000);//<--withdrawMoney() method call joint point

Trong ví dụ này, một joint point triệu gọi phương thức được thiết lập tại vị trí triệu gọi phương thức c.withdrawMoney(4000) đang xét.

Joint point triệu gọi phương thức khởi tạo (Constructor call joint point): xảy ra khi một phương thức khởi tạo đối tượng được triệu gọi trong quá trình tạo mới một

đối tượng. Joint point này xảy ra bên trong đối tượng hay ứng dụng triệu gọi phương phức khởi tạo đối tượng. Chúng ta xem xét đoạn mã sau:

ATMcard c = new ATMcard (0, 3000);

Trong ví dụ này, một joint point xảy ra khi lệnh new kích hoạt một joint point triệu gọi phương thức.

Join point thực thi phương thức (Method execution joint point): xảy ra khi một phương thức của một đối tượng được triệu và chuyển điều khiển cho phương thức được triệu gọi đó. Joint point này xảy ra trên đối tượng nhận lời gọi phương thức và trước khi các đoạn mã của phương thức được thực thi.

Joint point thực thi phương thức khởi tạo (Constructor execution joint point): giống như joint point thực thi phương thức nhưng xảy ra trước khi đoạn mã khởi tạo được thực thi. (adsbygoogle = window.adsbygoogle || []).push({});

Field get joint point: xảy ra khi một thuộc tính của một đối tượng được đọc, tham chiếu hay lấy giá trị. Đoạn mã sau đây minh họa một field get joint point xảy ra khi thuộc tính balanceAmount của lớp ATMCard được lấy giá trị:

class ATMCard {

int balanceAmount; …

public void printAccountBalance(){

System.out.print(“Balance amount is “

+ balanceAmount // <-- field get joint point + …);

} … }

Field set joint point: xảy ra khi một thuộc tính của một đối tượng được gán giá trị. Đoạn mã sau minh họa một field set joint point xảy ra khi thuộc tính balanceAmount được gán giá trị mới:

class ATMCard {

int balanceAmount; …

public void withdrawMoney(int amount){

balanceAmount-=amount; <-- field set joint point }

}

Exception handler execution joint point: xảy ra khi một xử lý ngoại lệ được thực thi.

Ngoài ra còn một số loại join point khác như: Class initialization join point, Object initialization join point, Object pre-initialization join point, Advice execution join point. Chi tiết các loại join point này có thể xem chi tiết trong các tài liệu [11, 13].

1.4.3. Pointcut

Pointcut là một cấu trúc chương trình lựa chọn ra một nhóm các join point và tập hợp ngữ cảnh tại các join point này. Nó chỉ ra một cách trực tiếp một mối quan tâm sẽ cắt ngang ứng dụng chính như thế nào. Pointcut hợp lệ ngang qua tất cả các đối tượng được thể hiện từ các lớp và nếu bạn có một số lượng lớn các đối tượng, bạn nên lường trước một số lượng lớn các so khớp. Pointcut dùng để chỉ ra các quy tắc đan còn join point là các vị trí thỏa mãn các quy tắc đó.

Một bộ mô tả pointcut (pointcut designator) dùng để xác định pointcut bằng tên hoặc bằng một biểu thức. Ta có thể khai báo một pointcut trong một aspect, một lớp hoặc một giao diện. Giống như dữ liệu và phương thức, ta có thể sử dụng một định danh phạm vi truy cập (public, private,...) để giới hạn quyền truy cập đến pointcut. Định dạng tổng quát của pointcut như sau:

<pointcut> ::= <access_type> <pointcut_name> ({ <parameters> }) : { designator [ && | || ] };

<access_type> ::= public | private [abstract] <pointcut_name> ::= { <identifier> }

<parameters> ::= { <identifier> <type> }

<designator> ::= [!]Call | execution | target | args | cflow | cflowbelow | staticinitialization |

within | if | adviceexecution | preinitialization

<identifier> ::= ký tự { ký tự | số } <type> ::= kiểu Java hợp lệ

Chúng ta đặt pointcut vào bên trong một cấu trúc aspect và sử dụng nó để so khớp các joint point trong mã. Kiểu truy cập của pointcut có thể là public hoặc private (tùy thuộc vào các lưu ý thiết kế) và chỉ ra phạm vi của pointcut trong aspect/class. Phạm vi truy cập mặc định là trong phạm vi gói (package) và do vậy chúng ta phải cung cấp kiểu truy cập cụ thể cần thiết cho pointcut. Sau kiểu truy cập là tên của pointcut. Tên này là chuỗi được sử dụng để biểu diễn pointcut bên trong aspect/class.

Tên tương tự như tên phương thức được sử dụng trong một lớp Java truyền thống. Bên trong ngữ cảnh của một aspect hay class, tất cả các pointcut phải có một tên duy nhất, điều đó có nghĩa là không nập chồng được pointcut trong AspectJ.

Tên của pointcut có thể chứa một số tham số. Các tham số này được sử dụng để chuyển giao ngữ cảnh được kéo từ một joint point. Ngữ cảnh có thể được chuyển giao tới một cấu trúc aspect hoặc các joint point khác.

Sau các tham số của pointcut là dấu hai chấm và một hoặc nhiều bộ mô tả pointcut. Bộ mô tả cung cấp một định nghĩa xung quanh joint point được sử dụng trong pointcut. Nếu một pointcut muốn có nhiều bộ mô tả, bạn phải sử dụng các toán tử lôgic để tạo sự kết nối.

Java cũng cấp khả năng tạo các cấu trúc không được đặt tên hay nặc danh. AspectJ cũng cung cấp khả năng này, trong một cấu trúc được gọi là pointcut nguyên bản (primitive pointcut). Pointcut nguyên bản không có tên và chỉ bao gồm các bộ mô tả joint point.

Như ta đã đề cập, pointcut lựa chọn một tập một hay nhiều joint point để xác định hành động dự định của chúng. Khi joint point được đạt tới trong mã ứng dụng chính, pointcut sẽ được kích hoạt và một số đoạn mã (advice) được thực thi tiềm tàng. Chúng ta sẽ chỉ ra bộ miêu tả hành động như thế nào trong một ứng dụng AspectJ. Tất cả các bộ mô tả được viết dưới định dạng sau: (adsbygoogle = window.adsbygoogle || []).push({});

designator ::= designator_identifier(<signature> | <typePattern> | <pointcut>)

<designator_identifier> ::= Call | execution | target | args | cflow | cflowbelow | staticinitialization |

within | if | adviceexecution | preinitialization

<typePattern> ::= Kiểu lớp Java <pointcut> - được định nghĩa ở trên

Tham số trong bộ miêu tả là một joint point signature hay một kiểu lớp joint point hay một pointcut khác. Một joint point signature nhìn chung biểu diễn chữ ký (signature) của một phương thức, một phương thức khởi tạo trong một lớp hay một kiểu. Có thể có một số ký tự đại diện trong chữ ký. Trong trường hợp một joint point khởi tạo phương thức, chữ ký bao gồm lời gọi hàm new(). Đối với một joint point kiểu lớp, tham số là tên của một lớp trong ứng dụng. Chúng ta cũng có thể sử dụng các ký tự đại diện với các kiểu lớp.

Để chỉ ra một nhóm các join point, phần chữ ký (signature) của pointcut chứa các ký tự đại diện (wildcard) và các toán tử. Trong AspectJ, có 3 loại ký tự đại diện và 2 loại toán tử.

Ký tự đại diện:

“*”: Chỉ ra số lượng bất kỳ các ký tự trừ dấu chấm (“.”). “..”: Chỉ ra số lượng bất kỳ các ký tự, chứa cả dấu chấm. “+”: Chỉ ra bất kỳ lớp con hoặc giao diện con nào. Toán tử:

Toán tử một ngôi: AspectJ chỉ cung cấp duy nhất một toán tử một ngôi “!” cho point cut: Toán tử phủ định cho phép phù hợp tất cả các join point trừ join point mà point cut chỉ ra bằng toán tử “!”.

Toán tử hai ngôi: “||” (Hoặc) và “&&” (Và) để kết nối các pointcut với nhau. Hình 1.9 minh họa ví dụ về định nghĩa pointcut với tên accountOperations(). Point cut này sẽ nắm bắt toàn bộ các phương thức trong lớp Account.

Hình 1.9. Ví dụ về định nghĩa pointcut

Sau đây chúng tôi liệt kê các bộ mô tả pointcut một cách ngắn ngọn, chi tiết về các bộ mô tả này có thể xem trong các tài liệu [11, 13]:

execution – So khớp sự thực thi của một phương thức hay một phương thức khởi tạo. Ví dụ:

pointcut p(): execution(int ATMCard.withdrawMoney(..)); call – So khớp các lời gọi tới một phương thức hay một phương thức khởi tạo. Ví dụ:

pointcut p(): call(int ATMCard.withdrawMoney(..));

initialization – So khớp sự thực thi của phương thức khởi tạo đầu tiên tới một lớp. Ví dụ:

handler – So khớp các ngoại lệ. Ví dụ:

pointcut p() : handler(Throwable+);

Một phần của tài liệu Nghiên cứu về kiểm chứng bất biến của đối tượng sử dụng lập trình hướng khía cạnh (Trang 26)