JavaRush /Blog Java /Random-VI /AOP là gì? Khái niệm cơ bản về lập trình hướng theo khía ...

AOP là gì? Khái niệm cơ bản về lập trình hướng theo khía cạnh

Xuất bản trong nhóm
Xin chào các bạn! Nếu không hiểu các khái niệm cơ bản, sẽ rất khó để đi sâu vào các khuôn khổ và cách tiếp cận để xây dựng chức năng. Vì vậy, hôm nay chúng ta sẽ nói về một trong những khái niệm này - AOP, hay lập trình hướng khía cạnh . AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 1Đây không phải là một chủ đề dễ dàng và thường không được sử dụng trực tiếp, nhưng nhiều framework và công nghệ sử dụng nó một cách kỹ lưỡng. Và tất nhiên, đôi khi trong các cuộc phỏng vấn, bạn có thể được yêu cầu cho bạn biết một cách chung chung đây là loại động vật nào và nó có thể được sử dụng ở đâu. Vì vậy, hãy xem xét các khái niệm cơ bản và một số ví dụ đơn giản về AOP trong Java . AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 2Vì vậy, AOP - lập trình hướng theo khía cạnh - là một mô hình nhằm tăng tính mô-đun của các phần khác nhau của ứng dụng bằng cách tách biệt các mối quan tâm xuyên suốt. Để thực hiện việc này, hành vi bổ sung sẽ được thêm vào mã hiện có mà không thay đổi mã gốc. Nói cách khác, chúng tôi dường như treo chức năng bổ sung lên trên các phương thức và lớp mà không thực hiện sửa đổi mã đã sửa đổi. Tại sao điều này là cần thiết? Sớm hay muộn chúng ta cũng đi đến kết luận rằng cách tiếp cận hướng đối tượng thông thường không phải lúc nào cũng giải quyết được một số vấn đề một cách hiệu quả. Vào thời điểm đó, AOP đến giải cứu và cung cấp cho chúng tôi các công cụ bổ sung để xây dựng ứng dụng. Và các công cụ bổ sung có nghĩa là tăng tính linh hoạt trong quá trình phát triển, nhờ đó có nhiều lựa chọn hơn để giải quyết một vấn đề cụ thể.

Ứng dụng của AOP

Lập trình hướng theo khía cạnh được thiết kế để giải quyết các vấn đề xuyên suốt, có thể là bất kỳ mã nào được lặp lại nhiều lần theo nhiều cách khác nhau, không thể cấu trúc hoàn toàn thành một mô-đun riêng biệt. Theo đó, với AOP, chúng ta có thể để phần này bên ngoài mã chính và xác định nó theo chiều dọc. Một ví dụ là việc áp dụng chính sách bảo mật trong một ứng dụng. Thông thường, bảo mật cắt ngang nhiều thành phần của ứng dụng. Hơn nữa, chính sách bảo mật ứng dụng phải được áp dụng như nhau cho tất cả các phần hiện có và mới của ứng dụng. Đồng thời, chính sách bảo mật được sử dụng có thể tự phát triển. Đây là nơi việc sử dụng AOP có thể có ích . Ngoài ra một ví dụ khác là ghi nhật ký . Có một số lợi thế khi sử dụng phương pháp ghi nhật ký AOP so với việc chèn ghi nhật ký theo cách thủ công:
  1. Mã ghi nhật ký rất dễ triển khai và xóa: bạn chỉ cần thêm hoặc xóa một vài cấu hình của một số khía cạnh.
  2. Tất cả mã nguồn để ghi nhật ký được lưu trữ ở một nơi và không cần phải tìm thủ công tất cả các nơi sử dụng.
  3. Mã dành cho việc ghi nhật ký có thể được thêm vào bất cứ đâu, có thể là các phương thức và lớp đã được viết sẵn hoặc chức năng mới. Điều này làm giảm số lượng lỗi của nhà phát triển.
    Ngoài ra, khi bạn xóa một khía cạnh khỏi cấu hình thiết kế, bạn có thể hoàn toàn chắc chắn rằng tất cả mã theo dõi đều bị xóa và không thiếu thứ gì.
  4. Các khía cạnh là mã độc lập có thể được sử dụng lại và cải tiến nhiều lần.
AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 3AOP cũng được sử dụng để xử lý ngoại lệ, lưu vào bộ đệm và loại bỏ một số chức năng để có thể tái sử dụng.

Khái niệm cơ bản về AOP

Để tiến xa hơn trong việc phân tích chủ đề, trước tiên chúng ta hãy làm quen với các khái niệm chính của AOP. Lời khuyên là logic, mã bổ sung, được gọi từ điểm kết nối. Lời khuyên có thể được thực hiện trước, sau hoặc thay vì điểm kết nối (xem thêm ở bên dưới). Các loại lời khuyên có thể :
  1. Trước - lời khuyên thuộc loại này được đưa ra trước khi thực hiện các phương thức đích - điểm kết nối. Khi sử dụng các khía cạnh làm lớp, chúng tôi lấy chú thích @Before để đánh dấu loại lời khuyên có trước. Khi sử dụng các khía cạnh dưới dạng tệp .aj , đây sẽ là phương thức before() .
  2. Sau (Sau) - lời khuyên được thực thi sau khi hoàn thành việc thực hiện các phương thức - điểm kết nối, cả trong trường hợp thông thường và khi ném ngoại lệ.
    Khi sử dụng các khía cạnh làm lớp, chúng ta có thể sử dụng chú thích @After để cho biết rằng đây là mẹo xuất hiện sau.
    Khi sử dụng các khía cạnh dưới dạng tệp .aj , đây sẽ là phương thức after() .
  3. Sau khi quay lại - những mẹo này chỉ được thực thi nếu phương thức đích hoạt động bình thường, không có lỗi.
    Khi các khía cạnh được biểu diễn dưới dạng các lớp, chúng ta có thể sử dụng chú thích @AfterReturning để đánh dấu lời khuyên là được thực thi sau khi hoàn thành thành công.
    Khi sử dụng các khía cạnh dưới dạng tệp .aj, đây sẽ là phương thức after() trả về (Object obj) .
  4. Sau khi ném - loại lời khuyên này dành cho những trường hợp khi một phương thức, tức là điểm kết nối, ném ra một ngoại lệ. Chúng tôi có thể sử dụng lời khuyên này để xử lý một số trường hợp thực thi không thành công (ví dụ: khôi phục toàn bộ giao dịch hoặc ghi nhật ký với mức theo dõi được yêu cầu).
    Đối với các lớp khía cạnh, chú thích @AfterThrowing được sử dụng để chỉ ra rằng lời khuyên này được sử dụng sau khi ném ra một ngoại lệ.
    Khi sử dụng các khía cạnh ở dạng tệp .aj , đây sẽ là phương thức - after() ném (Ngoại lệ e) .
  5. Xung quanh có lẽ là một trong những loại lời khuyên quan trọng nhất xung quanh một phương thức, tức là một điểm kết nối, chẳng hạn như chúng ta có thể chọn có thực hiện một phương thức điểm kết nối nhất định hay không.
    Bạn có thể viết mã tư vấn chạy trước và sau khi phương thức điểm nối thực thi.
    Trách nhiệm của lời khuyên xung quanh bao gồm việc gọi phương thức điểm tham gia và trả về các giá trị nếu phương thức đó trả về một cái gì đó. Nghĩa là, trong mẹo này, bạn có thể chỉ cần bắt chước hoạt động của phương thức đích mà không cần gọi nó và kết quả là trả về một cái gì đó của riêng bạn.
    Đối với các khía cạnh ở dạng lớp, chúng tôi sử dụng chú thích @Around để tạo các mẹo bao bọc điểm kết nối. Khi sử dụng các khía cạnh dưới dạng tệp .aj , đây sẽ là phương thức Around() .
Điểm tham gia - một điểm trong chương trình thực thi (gọi một phương thức, tạo đối tượng, truy cập một biến) nơi nên áp dụng lời khuyên. Nói cách khác, đây là một số loại biểu thức chính quy, với sự trợ giúp của những vị trí giới thiệu mã (nơi áp dụng các mẹo) được tìm thấy. Một pointcut là một tập hợp các điểm kết nối . Việc cắt xác định xem một điểm kết nối nhất định có phù hợp với một đầu nhất định hay không. Khía cạnh là một mô-đun hoặc lớp thực hiện chức năng đầu cuối. Một khía cạnh sửa đổi hành vi của phần còn lại của mã bằng cách áp dụng lời khuyên tại các điểm nối được xác định bởi một số lát cắt . Nói cách khác, nó là sự kết hợp của các mẹo và điểm kết nối. Giới thiệu - thay đổi cấu trúc của một lớp và/hoặc thay đổi hệ thống phân cấp kế thừa để thêm chức năng khía cạnh vào mã nước ngoài. Mục tiêu là đối tượng mà lời khuyên sẽ được áp dụng. Dệt là quá trình liên kết các khía cạnh với các đối tượng khác để tạo ra các đối tượng proxy được đề xuất. Điều này có thể được thực hiện tại thời điểm biên dịch, thời gian tải hoặc thời gian chạy. Có ba kiểu dệt:
  • Dệt trong thời gian biên dịch - Nếu bạn có mã nguồn của một khía cạnh và mã mà bạn sử dụng các khía cạnh, bạn có thể biên dịch trực tiếp mã nguồn và khía cạnh đó bằng trình biên dịch AspectJ;
  • dệt sau biên dịch (dệt nhị phân) - nếu bạn không thể hoặc không muốn sử dụng các phép biến đổi mã nguồn để dệt các khía cạnh vào mã của mình, bạn có thể lấy các lớp hoặc lọ đã được biên dịch và chèn các khía cạnh;
  • việc dệt trong thời gian tải chỉ đơn giản là việc dệt nhị phân được trì hoãn cho đến khi trình nạp lớp tải tệp lớp và xác định lớp cho JVM.
    Để hỗ trợ điều này, cần có một hoặc nhiều "bộ nạp lớp dệt". Chúng được cung cấp rõ ràng bởi bộ thực thi hoặc được kích hoạt bởi "tác nhân dệt".
AspectJ là một triển khai cụ thể của mô hình AOP nhằm triển khai khả năng giải quyết các vấn đề xuyên suốt. Tài liệu có thể được tìm thấy ở đây .

Ví dụ trong Java

Tiếp theo, để hiểu rõ hơn về AOP, chúng ta sẽ xem xét các ví dụ nhỏ về cấp độ Hello World. AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 4Hãy để tôi lưu ý ngay rằng trong các ví dụ của chúng tôi, chúng tôi sẽ sử dụng tính năng dệt trong thời gian biên dịch . Trước tiên, chúng ta cần thêm phần phụ thuộc sau vào pom.xml :
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Theo quy định, một trình biên dịch Ajs đặc biệt được sử dụng để sử dụng các khía cạnh . IntelliJ IDEA mặc định không có nó, vì vậy khi chọn nó làm trình biên dịch ứng dụng, bạn cần chỉ định đường dẫn đến bản phân phối AspectJ . Bạn có thể đọc thêm về phương pháp chọn Ajs làm trình biên dịch trên trang này. Đây là phương pháp đầu tiên và phương pháp thứ hai (tôi đã sử dụng) là thêm plugin sau vào pom.xml :
<build>
  <plugins>
     <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.7</version>
        <configuration>
           <complianceLevel>1.8</complianceLevel>
           <source>1.8</source>
           <target>1.8</target>
           <showWeaveInfo>true</showWeaveInfo>
           <verbose>true</verbose>
           <Xlint>ignore</Xlint>
           <encoding>UTF-8</encoding>
        </configuration>
        <executions>
           <execution>
              <goals>
                 <goal>compile</goal>
                 <goal>test-compile</goal>
              </goals>
           </execution>
        </executions>
     </plugin>
  </plugins>
</build>
Sau đó, nên nhập lại từ Maven và chạy mvn clean biên dịch . Bây giờ chúng ta hãy chuyển sang các ví dụ.

Ví dụ số 1

Hãy tạo một lớp Main . Trong đó chúng ta sẽ có một điểm khởi chạy và một phương thức in các tên được truyền cho nó trong bảng điều khiển:
public class Main {

  public static void main(String[] args) {
  printName("Толя");
  printName("Вова");
  printName("Sasha");
  }

  public static void printName(String name) {
     System.out.println(name);
  }
}
Không có gì phức tạp: họ chuyển tên và hiển thị nó trong bảng điều khiển. Nếu chúng ta chạy nó ngay bây giờ, giao diện điều khiển sẽ hiển thị:
Tolya Vova Sasha
Chà, đã đến lúc tận dụng sức mạnh của AOP. Bây giờ chúng ta cần tạo một tập tin -spect . Chúng có hai loại: loại thứ nhất là tệp có phần mở rộng .aj , loại thứ hai là lớp thông thường triển khai các khả năng AOP bằng cách sử dụng chú thích. Trước tiên chúng ta hãy xem một tệp có phần mở rộng .aj :
public aspect GreetingAspect {

  pointcut greeting() : execution(* Main.printName(..));

  before() : greeting() {
     System.out.print("Привет ");
  }
}
Tập tin này có phần giống với một lớp. Hãy cùng tìm hiểu điều gì đang xảy ra ở đây: pointcut - một vết cắt hoặc một tập hợp các điểm kết nối; lời chào() - tên của lát cắt này; : thực thi - khi thực thi * - tất cả, gọi - Main.printName(..) - phương thức này. Tiếp theo là lời khuyên cụ thể - before() - được thực thi trước khi phương thức đích được gọi, : hello() - lát cắt mà lời khuyên này phản ứng và bên dưới chúng ta thấy phần thân của chính phương thức đó, được viết bằng Java ngôn ngữ chúng ta hiểu. Khi chúng tôi chạy main với khía cạnh này, chúng tôi sẽ nhận được kết quả đầu ra sau cho bảng điều khiển:
Xin chào Tolya Xin chào Vova Xin chào Sasha
Chúng ta có thể thấy rằng mọi lệnh gọi phương thức printName đều đã được sửa đổi theo một khía cạnh. Bây giờ chúng ta hãy xem khía cạnh này sẽ trông như thế nào, nhưng dưới dạng một lớp Java có các chú thích:
@Aspect
public class GreetingAspect{

  @Pointcut("execution(* Main.printName(String))")
  public void greeting() {
  }

  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Привет ");
  }
}
Sau tệp khía cạnh .aj , mọi thứ rõ ràng hơn:
  • @Aspect biểu thị rằng lớp đã cho là một khía cạnh;
  • @Pointcut("execution(* Main.printName(String))") là một điểm cắt kích hoạt tất cả các cuộc gọi đến Main.printName với đối số đến thuộc loại String ;
  • @Before("greeting()") - lời khuyên được áp dụng trước khi gọi mã được mô tả tại điểm cắt lời chào() .
Chạy main với khía cạnh này sẽ không thay đổi đầu ra của bàn điều khiển:
Xin chào Tolya Xin chào Vova Xin chào Sasha

Ví dụ số 2

Giả sử chúng ta có một số phương thức thực hiện một số thao tác cho máy khách và gọi phương thức này từ main :
public class Main {

  public static void main(String[] args) {
  makeSomeOperation("Толя");
  }

  public static void makeSomeOperation(String clientName) {
     System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  }
}
Sử dụng chú thích @Around , hãy thực hiện một số việc như “giao dịch giả”:
@Aspect
public class TransactionAspect{

  @Pointcut("execution(* Main.makeSomeOperation(String))")
  public void executeOperation() {
  }

  @Around(value = "executeOperation()")
  public void beforeAdvice(ProceedingJoinPoint joinPoint) {
     System.out.println("Открытие транзакции...");
     try {
        joinPoint.proceed();
        System.out.println("Закрытие транзакции....");
     }
     catch (Throwable throwable) {
        System.out.println("Операция не удалась, откат транзакции...");
     }
  }
  }
Bằng cách sử dụng phương thức tiến hành của đối tượng ContinueingJoinPoint , chúng ta gọi phương thức của trình bao bọc để xác định vị trí của nó trong bảng và theo đó, mã trong phương thức trên joinPoint.proceed(); - đây là Trước , ở dưới - Sau . Nếu chúng ta chạy main chúng ta sẽ vào được console:
Mở giao dịch... Thực hiện một số thao tác cho khách hàng - Tolya Đóng giao dịch....
Nếu chúng ta thêm một lần ném ngoại lệ vào phương thức của mình (đột nhiên thao tác không thành công):
public static void makeSomeOperation(String clientName)throws Exception {
  System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  throw new Exception();
}
Sau đó chúng ta sẽ nhận được kết quả đầu ra trong bảng điều khiển:
Mở giao dịch... Thực hiện một số thao tác cho khách hàng - Tolya Thao tác không thành công, giao dịch đã được khôi phục...
Nó hóa ra là một quá trình xử lý thất bại giả.

Ví dụ số 3

Ví dụ tiếp theo, hãy làm một việc như đăng nhập vào bảng điều khiển. Trước tiên, hãy xem Main , nơi diễn ra logic nghiệp vụ giả của chúng tôi:
public class Main {
  private String value;

  public static void main(String[] args) throws Exception {
     Main main = new Main();
     main.setValue("<некоторое meaning>");
     String valueForCheck = main.getValue();
     main.checkValue(valueForCheck);
  }

  public void setValue(String value) {
     this.value = value;
  }

  public String getValue() {
     return this.value;
  }

  public void checkValue(String value) throws Exception {
     if (value.length() > 10) {
        throw new Exception();
     }
  }
}
Trong main , sử dụng setValue chúng ta sẽ đặt giá trị của biến nội bộ - value , sau đó sử dụng getValue chúng ta sẽ lấy giá trị này và trong checkValue chúng ta sẽ kiểm tra xem giá trị này có dài hơn 10 ký tự hay không. Nếu có, một ngoại lệ sẽ được ném ra. Bây giờ hãy xem khía cạnh mà chúng ta sẽ ghi lại hoạt động của các phương thức:
@Aspect
public class LogAspect {

  @Pointcut("execution(* *(..))")
  public void methodExecuting() {
  }

  @AfterReturning(value = "methodExecuting()", returning = "returningValue")
  public void recordSuccessfulExecution(JoinPoint joinPoint, Object returningValue) {
     if (returningValue != null) {
        System.out.printf("Успешно выполнен метод - %s, класса- %s, с результатом выполнения - %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName(),
              returningValue);
     }
     else {
        System.out.printf("Успешно выполнен метод - %s, класса- %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName());
     }
  }

  @AfterThrowing(value = "methodExecuting()", throwing = "exception")
  public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
     System.out.printf("Метод - %s, класса- %s, был аварийно завершен с исключением - %s\n",
           joinPoint.getSignature().getName(),
           joinPoint.getSourceLocation().getWithinType().getName(),
           exception);
  }
}
Những gì đang xảy ra ở đây? @Pointcut("execution(* *(..))") - sẽ kết nối với tất cả các cuộc gọi đến tất cả các phương thức; @AfterReturning(value = "methodExecuting()", return = "returningValue") - lời khuyên sẽ được thực thi sau khi phương thức đích hoàn thành thành công. Chúng tôi có hai trường hợp ở đây:
  1. Khi một phương thức có giá trị trả về if (returningValue != null) {
  2. Khi không có giá trị trả về else {
@AfterThrowing(value = "methodExecuting()", Throwing = "Exception") - lời khuyên sẽ được kích hoạt trong trường hợp có lỗi, tức là khi một ngoại lệ được ném ra khỏi phương thức. Và theo đó, bằng cách chạy main , chúng ta sẽ có được một kiểu đăng nhập vào bảng điều khiển:
Phương thức - setValue, của lớp - Main đã được thực thi thành công Phương thức - getValue, của lớp - Main, được thực thi thành công, với kết quả thực thi - <some value> Phương thức - checkValue, của lớp - Main, đã bị chấm dứt một cách bất thường với một ngoại lệ - Phương thức java.lang.Exception - main, class-Main, bị lỗi với một ngoại lệ - java.lang.Exception
Chà, vì chúng tôi không xử lý ngoại lệ nên chúng tôi cũng sẽ lấy dấu vết ngăn xếp của nó: AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 5Bạn có thể đọc về các ngoại lệ và cách xử lý chúng trong các bài viết sau: Ngoại lệ trong JavaNgoại lệ và cách xử lý của chúng . Đó là tất cả đối với tôi ngày hôm nay. Hôm nay chúng ta làm quen với AOP , và các bạn có thể thấy con thú này không đáng sợ như miêu tả. Tạm biệt tất cả mọi người!AOP là gì?  Nguyên tắc cơ bản của lập trình hướng khía cạnh - 6
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION