Spring AOP
AOP là từ viết tắt của Aspect Oriented Programming, dịch ra tiếng Việt là “phương pháp lập trình hướng khía cạnh”. AOP là một kỹ thuật lập trình cho phép phân tách chương trình thành cách module riêng lẻ, không phụ thuộc nhau. Khi hoạt động, chương trình sẽ kết hợp các module lại để thực hiện các chức năng nhưng khi sửa đổi chức năng thì chỉ cần sửa đổi trên một module cụ thể.
AOP còn được gọi là Aspect Oriented Software Development (AOSD) là một nguyên tắc thiết kế, giúp tách rời các yêu cầu hay các vấn đề được quan tâm (separation of concerns) trong chương trình thành các thành phần độc lập và từ đó tăng tính uyển chuyển cho chương trình. Trong Separation of concerns, người ta cho rằng những vấn đề tương tự nhau nên được giải quyết trong một unit of code. Khi lập trình thủ tục (functional programming), một unit of code là một function/ method, còn trong lập trình hướng đối tượng (OOP) thì unit of code là một class.
Trong khi xây dựng các chương trình ứng dụng, có rất nhiều những vấn đề liên quan đến phần mềm mà chúng ta cần quan tâm. Chẳng hạn, chúng ta xây dựng một hệ thống đăng ký tạo tài khoản cho một ngân hàng. Ngoài công việc chính cho phép người dùng có thể tạo tài khoản (core concern), hệ thống còn phải đảm bảo các vấn đề khác (cross-cutting concern) như chứng thực người dùng, kiểm tra ràng buộc, quản lý transaction, xử lý ngoại lệ, ghi log, debug, đo hiệu năng của ứng dụng, …
1
2
3
4
5
6
7
8
9
10
11
12
13
Logger logger = Logger.getLogger(...);
TransactionManager tm = getTransactionManager();
public void addAccount(Account account) {
logger.info("Creating (" + account + ") Account");
try {
tm.beginTransaction();
db.add(account);
tm.commit();
} catch (Exception) {
tm.rollback();
logger.error("Account creation failed");
}
}
Như bạn thấy, logic của chương trình của chúng ta phải làm rất nhiều việc như ghi log, mở/ đóng transaction, xử lý ngoại lệ, … Khi có nhiều phương thức tương tự vậy trong ứng dụng, code chúng ta bị liên kết chặt vào nhau, duplicate code, phân mảnh nhiều nơi, khó khăn khi sửa đổi thay thêm logic mới, … Để giải quyết vấn đề này, chúng ta có thể sử dụng kỹ thuật AOP. Trong AOP, chương trình của chúng ta được chia thành 2 loại concern:
- Core concern/ Primary concern: là requirement, logic xử lý chính của chương trình.
- Cross-cutting concern: là những logic xử lý phụ cần được thực hiện của chương trình khi core concern được gọi như security, logging, tracing, monitoring, …
1. Các khái niệm cốt lõi của AOP
Muốn nắm được trọng tâm và vận dụng AOP trong lập trình Java Web bạn phải nắm vững các kiến thức quan trọng, mà cốt lõi là các khái niệm sau đây:
- Aspect: là một lớp thực thi sự “quan tâm”của các ứng dụng doanh nghiệp qua nhiều lớp khác, chẳng hạn như quản lý giao dịch. Các Aspect có thể là một lớp bình thường được cấu hình thông qua Spring XML hoặc chúng ta có thể sử dụng tích hợp Spring AspectJ để xác định một lớp dưới dạng Aspect bằng cách sử dụng chú thích @Aspect.
- Join Point: là một điểm cụ thể trong ứng dụng như thực thi phương thức, xử lý ngoại lệ, thay đổi giá trị biến đối tượng, …. Trong Spring AOP, Join Point luôn là nơi thực thi một phương thức.
- Advice: là các hành động được thực hiện cho một Join point cụ thể. Về mặt lập trình, chúng là các phương thức được thực thi khi đạt đến một join point nhất định với điểm cắt phù hợp trong ứng dụng. Bạn có thể coi Advice là Struts2 interceptor hoặc Servlet Filter.
- Pointcut: là các biểu thức được kết hợp với các join point để xác định xem Advice có cần được thực hiện hay không. Pointcut sử dụng các loại biểu thức khác nhau được khớp với các join point và Spring framework sử dụng ngôn ngữ biểu thức pointcut AspectJ.
- Target Object: là đối tượng mà các Advice được áp dụng. Spring AOP được triển khai bằng proxy runtime nên đối tượng này luôn là đối tượng được ủy quyền. Điều này có nghĩa là một lớp con được tạo trong thời gian chạy - nơi phương thức đích bị ghi đè và Advice được đưa vào dựa trên cấu hình của chúng.
- AOP proxy: Spring AOP triển khai sử dụng JDK dynamic proxy để tạo cá lớp Proxy với các lớp Target và lời gọi Advice – chúng được gọi là các lớp Proxy AOP. Chúng ta có thể sử dụng CGLIB proxy bằng cách thêm nó như một dependency trong dự án Spring AOP.
- Weaving: Là quá trình liên kết các Aspect với các đối tượng khác để tạo ra các đối tượng proxy. Điều này có thể được thực hiện tại thời gian biên dịch, thời gian tải hoặc thời gian chạy. Spring AOP thực hiện Weaving trong thời gian chạy.
Các loại AOP Advice:
- Before Advice: Những advice này chạy trước khi thực hiện các phương thức join point. Chúng ta có thể sử dụng chú thích @Before để đánh dấu loại advice này.
- After (finally) Advice: Một advice được thực thi sau khi phương thức join point kết thúc thực thi, cho dù bình thường hay bằng cách ném một ngoại lệ. Chúng ta có thể tạo After Advice bằng cách sử dụng chú thích @After.
- After Returning Advice: Đôi khi chúng ta muốn các phương thức advice chỉ thực thi nếu phương thức join point thực thi bình thường. Chúng ta có thể sử dụng chú thích @AfterReturning để đánh dấu một phương thức là After Returning Advice.
- After Throwing Advice: Advice này chỉ được thực hiện khi phương thức join point ném ra ngoại lệ, chúng ta có thể sử dụng nó để khôi phục giao dịch. Sử dụng chú thích @AfterThrowing cho loại advice này.
- Around Advice: Đây là advice quan trọng và mạnh mẽ nhất. Advice này xoay quanh phương thức join point và chúng ta cũng có thể chọn có thực hiện phương thức join point hay không. Chúng ta có thể viết code advice được thực thi trước và sau khi thực thi phương thức join point. Trách nhiệm của Around Advice là gọi phương thức join point và trả về giá trị nếu phương thức trả về thứ gì đó. Chúng ta sẽ sử dụng chú thích @Around để tạo các phương thức Around Advice. Những điểm được đề cập ở trên nghe có vẻ khó hiểu nhưng khi chúng ta triển khai Spring AOP, mọi thứ sẽ rõ ràng hơn. Hãy bắt đầu tạo một dự án Spring đơn giản với các triển khai AOP. Spring cung cấp hỗ trợ cho việc sử dụng các chú thích AspectJ để tạo các Aspect và chúng ta sẽ sử dụng nó vì nó quá đơn giản. Tất cả các chú thích AOP ở trên đều được định nghĩa trong package org.aspectj.lang.annotation.
2. Ví dụ demo
Spring AOP AspectJ Dependency
Spring Framework cung cấp hỗ trợ AOP theo mặc định nhưng vì chúng ta đang sử dụng các chú thích AspectJ để cấu hình các Aspect và Advice nên chúng ta cần đưa chúng vào file pom.xml.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>SpringAOPExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<!-- Generic properties -->
<java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Spring -->
<spring-framework.version>4.0.2.RELEASE</spring-framework.version>
<!-- Logging -->
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<!-- Test -->
<junit.version>4.11</junit.version>
<!-- AspectJ -->
<aspectj.version>1.7.4</aspectj.version>
</properties>
<dependencies>
<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
Employee.java: (Một Java bean đơn giản)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Employee {
private String name;
public String getName() {
return name;
}
@Loggable
public void setName(String nm) {
this.name = nm;
}
public void throwException() {
throw new RuntimeException("Dummy Exception");
}
}
Một lớp EmployeeService.java để làm việc với Bean
1
2
3
4
5
6
7
8
9
10
11
12
public class EmployeeService {
private Employee employee;
public Employee getEmployee() {
return this.employee;
}
public void setEmployee(Employee e) {
this.employee = e;
}
}
Chúng ta có thể đã sử dụng chú thích Spring để cấu hình nó như một Spring Component nhưng chúng ta sẽ sử dụng cấu hình dựa trên XML trong project này. Lớp EmployeeService rất chuẩn và chỉ cung cấp cho một điểm truy cập cho các bean Employee.
Spring Bean Configuration với AOP
Nếu bạn đang sử dụng Spring Tool Suite, bạn có tùy chọn tạo “Spring Bean Configuration File” và “AOP schema namespace” nhưng nếu bạn đang sử dụng một số IDE khác, bạn có thể chỉ cần thêm nó vào file cấu hình Spring bean.
File cấu hình Bean dự án trông giống như bên dưới.
spring.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />
<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="spring.model.Employee">
<property name="name" value="Dummy Name"></property>
</bean>
<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="spring.service.EmployeeService">
<property name="employee" ref="employee"></property>
</bean>
<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="spring.aspect.EmployeeAnnotationAspect" />
</beans>
Để sử dụng Spring AOP trong Spring Bean ta cần làm như sau:
- Khai báo AOP namespace như sau:
xmlns:aop=”https://www.springframework.org/schema/aop” - Thêm phần tử
aop:aspectj-autoproxyđể bật hỗ trợ Spring AspectJ với proxy runtime tự động. - Cấu hình lớp Aspect như các Spring Bean khác.
Bạn có thể thấy rằng chúng ta có rất nhiều Aspect được xác định trong file cấu hình Spring Bean. Chúng ta hay cùng xem qua từng Aspect.
Spring AOP Before Aspect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspect {
@Before("execution(public String getName())")
public void getNameAdvice() {
System.out.println("Executing Advice on getName()");
}
@Before("execution(* spring.service.*.get*())")
public void getAllAdvice() {
System.out.println("Service method getter called");
}
}
Một số điểm quan trọng trong lớp Aspect bên trên là:
- Các lớp Aspect bắt buộc phải có chú thích
@Aspect. @Beforeannotation được sử dụng để tạo Before advice- Tham số String được truyền trong chú thích
@Beforelà biểu thức Pointcut getNameAdvice()advice sẽ thực thi cho bất kỳ phương thức Spring Bean nào có dấupublic String getName(). Đây là một điểm rất quan trọng cần nhớ, nếu chúng ta tạoEmployeebean bằng toán tử mới, các advice sẽ không được áp dụng, trừ khi chúng ta sử dụng ApplicationContext để lấy bean.- Chúng ta có thể sử dụng dấu hoa thị (*) làm thẻ đại diện trong biểu thức Pointcut, getAllAdvice() sẽ được áp dụng cho tất cả các lớp trong package spring.service có tên bắt đầu bằng get và không nhận bất kỳ đối số nào.
Các phương thức và tái sử dụng Spring AOP Pointcut
Đôi khi chúng ta phải sử dụng cùng một biểu thức Pointcut ở nhiều vị trí, chúng ta có thể tạo một phương thức trống với chú thích @Pointcut và sau đó sử dụng nó như một biểu thức trong các advice.
EmployeeAspectPointcut.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class EmployeeAspectPointcut {
@Before("getNamePointcut()")
public void loggingAdvice() {
System.out.println("Executing loggingAdvice on getName()");
}
@Before("getNamePointcut()")
public void secondAdvice() {
System.out.println("Executing secondAdvice on getName()");
}
@Pointcut("execution(public String getName())")
public void getNamePointcut() {
}
@Before("allMethodsPointcut()")
public void allServiceMethodsAdvice() {
System.out.println("Before executing service method");
}
// Pointcut thực thi trên tất cả các phương thức của các lớp trong một package
@Pointcut("within(spring.service.*)")
public void allMethodsPointcut() {
}
}
Trong đoạn code trên, chúng ta đang sử dụng đối số chú thích advice thay vì biểu thức.
Spring AOP JoinPoint và các tham số Advice
Chúng ta có thể sử dụng JoinPoint làm tham số trong các phương thức advice và sử dụng nó để nhận được ký số của các phương thức hoặc đối tượng đích.
Chúng ta có thể sử dụng biểu thức args() trong pointcut để áp dụng cho bất kỳ phương thức nào phù hợp với đối số mẫu. Nếu làm như vậy chúng ta cần sử dụng cùng một tên trong phương thức advice mà từ đó loại đối số được xác định. Chúng ta cũng có thể sử dụng các đối tượng Generic trong các đối số advice.
EmployeeAspectJoinPoint.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAspectJoinPoint {
@Before("execution(public void spring.model..set*(*))")
public void loggingAdvice(JoinPoint joinPoint) {
System.out.println("Before running loggingAdvice on method=" + joinPoint.toString());
System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));
}
// Đối số Advice sẽ được áp dụng cho các phương thức Bean với đối số String
@Before("args(name)")
public void logStringArguments(String name) {
System.out.println("String argument passed=" + name);
}
}
Spring AOP After Advice
Chúng ta cùng xem qua một Aspect khác của Advice: After Advice, After Throwing và After Returning advice.
EmployeeAfterAspect.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAfterAspect {
@After("args(name)")
public void logStringArguments(String name) {
System.out.println("Running After Advice. String argument passed=" + name);
}
@AfterThrowing("within(spring.model.Employee)")
public void logExceptions(JoinPoint joinPoint) {
System.out.println("Exception thrown in Employee Method=" + joinPoint.toString());
}
@AfterReturning(pointcut = "execution(* getName())", returning = "returnString")
public void getNameReturningAdvice(String returnString) {
System.out.println("getNameReturningAdvice executed. Returned String=" + returnString);
}
}
Chúng ta có thể sử dụng within trong biểu thức pointcut để áp dụng advice cho tất cả các phương thức trong lớp. Chúng ta có thể sử dụng advice @AfterReturning để lấy đối tượng trả về theo phương thức đã thông báo.
Chúng ta có phương thức throwException() trong Bean Employee để giới thiệu việc sử dụng advice After Throwing.
Spring AOP Around Aspect
Như đã giải thích trước đó, chúng ta có thể sử dụng Around aspect để cắt giảm việc thực thi phương thức trước và sau đó.
Chúng ta có thể sử dụng nó để kiểm soát xem phương thức được thông báo có thực thi hay không. Chúng ta cũng có thể kiểm tra giá trị trả về và thay đổi nó. Đây là advice mạnh mẽ nhất và cần được áp dụng đúng cách.
EmployeeAroundAspect.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class EmployeeAroundAspect {
@Around("execution(* spring.model.Employee.getName())")
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("After invoking getName() method. Return value=" + value);
return value;
}
}
Around advice luôn bắt buộc phải có ProceedingJoinPoint làm đối số và chúng ta nên sử dụng phương thức process() để gọi đối tượng advice mục tiêu.
Nếu phương thức advice trả về điều gì đó, advice có trách nhiệm trả lại cho chương trình đang gọi. Đối với các phương thức void, phương thức advice có thể trả về null.
Với around advice, chúng ta có thể kiểm soát đầu vào và đầu ra của phương thức cũng như hành vi thực thi của nó.
Spring Advice với Custom Annotation Pointcut
Nếu bạn xem xét tất cả các biểu thức advice ở trên, có khả năng chúng sẽ được áp dụng cho một số loại Bean khác mà không đúng như ý muốn. Chẳng hạn ai đó có thể định nghĩa một Spring Bean mới bằng phương thức getName() và advice sẽ bắt đầu được áp dụng cho nó ngay cả khi nó không được dự định. Đó là lý do tại sao chúng ta nên giữ cho phạm vi của biểu thức pointcut càng hẹp càng tốt.
Một cách tiếp cận khác là tạo chú thích tùy chỉnh (Custom Annotation) và chú thích các phương thức mà chúng ta muốn áp dụng advice. Đây là mục đích của phương thức Employee setName() được chú thích bằng @Loggable.
Loggable.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public @interface Loggable {
}
EmployeeAnnotationAspect.java:
package spring.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class EmployeeAnnotationAspect {
@Before("@annotation(spring.aspect.Loggable)")
public void myAdvice() {
System.out.println("Executing myAdvice!!");
}
}
Phương thức myAdvice() sẽ advice cho phương thức setName(). Đây là một cách tiếp cận rất an toàn và bất cứ khi nào chúng ta muốn áp dụng advice về bất kỳ phương thứcc nào, tất cả những gì chúng ta cần là chú thích nó bằng chú thích @Loggable.
Spring AOP XML Configuration
Chúng ta luôn thích chú thích hơn nhưng cũng có tùy chọn để cấu hình các aspect trong file cấu hình Spring.
Ví dụ - chúng ta có một lớp như bên dưới.
EmployeeXMLConfigAspect.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.aspectj.lang.ProceedingJoinPoint;
public class EmployeeXMLConfigAspect {
public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
Object value = null;
try {
value = proceedingJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value=" + value);
return value;
}
}
Chúng ta có thể cấu hình nó bằng cách đưa cấu hình sau vào file cấu hình Spring Bean.
1
2
3
4
5
6
7
8
9
<bean name="employeeXMLConfigAspect" class="spring.aspect.EmployeeXMLConfigAspect" />
<!-- Spring AOP XML Configuration -->
<aop:config>
<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
<aop:pointcut expression="execution(* spring.model.Employee.getName())" id="getNamePointcut"/>
<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
</aop:aspect>
</aop:config>
Spring AOP
Sau đây là chương trình đơn giản để chúng ta cùng nhìn lại tất cả những gì đã làm ở trên. Hãy thực thi nó và kiểm tra lại kiến thức nhé.
SpringMain.java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring.service.EmployeeService;
public class SpringMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
System.out.println(employeeService.getEmployee().getName());
employeeService.getEmployee().setName("TheCorleone");
employeeService.getEmployee().throwException();
ctx.close();
}
}
3. Lợi ích khi sử dụng AOP – Ưu nhược điểm
Spring AOP không tự dưng được sinh ra và cũng không bất thình lình mà tồn tại trong suốt quãng thời gian lịch sử của Java Web. Để trả lời cho câu hỏi tại sao, chúng ta hãy thử điểm qua các lợi ích và ưu điểm của Spring AOP xem nào.
- Lợi ích:
- Tăng hiệu quả của Object-orented programming (OOP).
- AOP không phải dùng để thay thế OOP mà để bổ sung cho OOP - nơi mà OOP còn thiếu sót trong việc tạo những ứng dụng thuộc loại phức tạp.
- Tăng cường tối đa khả năng tái sử dụng của mã nguồn.
- Đảm bảo Single responsibility principle: mỗi một module chỉ làm cái mà nó cần phải làm.
- Tuân thủ nguyên tắc “You aren’t gonna need it – YAGNI” – chúng ta chỉ cài đặt những thứ chúng ta thực sự cần, không bao giờ làm trước.
- Module hóa ở mức tiến trình / chức năng.
- Code gọn gàng hơn do tách biệt phần xử lý chính và phần xử lý liên quan.
- Chức năng chính của chương trình không cần biết đến các chức năng phụ khác.
- Các chức năng phụ có thể được thêm thắt, bật tắt tại thời điểm run-time tùy theo yêu cầu.
- Các thay đổi nếu có đối với các chức năng phụ sẽ không ảnh hưởng đến chương trình chính.
- Hệ thống sẽ uyển chuyển và giảm thiểu tính phụ thuộc lẫn nhau của các module.
- Ưu điểm:
- Thiết kế đơn giản: “You aren’t gonna need it (YAGNI)” – chúng ta chỉ cài đặt những thứ chúng ta thực sự cần mà không bao giờ cài đặt trước.
- Cài đặt chương trình một cách trong sáng: mỗi một module chỉ làm cái mà nó cần phải làm, giải quyết được hai vấn đề code tangling và code scattering.
- Tái sử dụng dễ dàng.
- Nhược điểm:
- Khái nhiệm khá trừu tượng, độ trừu tượng của chương trình cao
- Luồng chương trình phức tạp.
Bài viết là sự chia sẻ quá đầy đủ về Spring AOP và hi vọng nó sẽ giúp ích cho những bạn đang trong những bước đầu tìm hiểu về Java Web. Quá trình học và tiếp thu chưa bao giờ là dễ dàng cả, do đó chúng ta hãy từ từ - chậm mà chắc để có nền tảng vững chắc cho những kiến thức quan trọng phía sau.
Bài viết mang tính chất “ghi chú, lưu trữ, chia sẻ và phi lợi nhuận”.
Nếu bạn thấy hữu ích, đừng quên chia sẻ với bạn bè và đồng nghiệp của mình nhé!
Happy coding! 😎 👍🏻 🚀 🔥
Đọc thêm: