如何理解 Spring 中的 AOP
一、AOP 的概述
-
AOP(Aspect Oriented Programming):面向切面編程,通過預編譯方式和運行期動態代理來實現程序功能的統一維護的一種技術。
-
AOP是OOP(面向對象編程)的延續,是 Spring 框架中重要內容,是函數式編程的一種衍生範型。
-
利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
-
AOP採取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼
縱向繼承。 -
經典應用場景:事務管理、性能監視、安全檢查、緩存 、日誌等。
-
Spring AOP使用純 Java 實現,不需要專門的編譯過程和類加載器,在運行期通過代理方式向目標類織入增強代碼。
-
AspectJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入。
-
Spring 中的 AOP 實現原理
a、接口 + 實現類 :Spring採用 jdk 的動態代理Proxy。
b、實現類:Spring 採用 cglib字節碼增強。
a、Aop底層將採用代理機制進行實現**(最底層也是依賴反射)**。
二、AOP 實現案例
1、對 AOP 的理解(畫圖解釋)
2、AOP 中通過 JDK動態代理簡單案例演示
接口、實現類和切面類
public interface UserService {
void addUser();
void updateUser();
int deleteUser(int id);
}
=========================================================================================
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加用戶");
}
@Override
public void updateUser() {
System.out.println("更新用戶");
}
@Override
public int deleteUser(int id) {
System.out.println("通過id刪除用戶");
return 1;
}
}
切面類
public class MyAspect {
public void before(){
System.out.println("開啓事務");
}
public void after(){
System.out.println("提交事務");
}
}
JDK實現動態代理
public class MyBeanFactory {
// JDK實現動態代理
public static UserService createUserService() {
//1.創建目標對象target
final UserService userService = new UserServiceImpl();
//2.聲明切面類對象
final MyAspect aspect = new MyAspect();
//3.把切面類2個方法應用於目標類
//3.1 創建JDK代理,攔截方法
UserService serviceProxy = (UserService) Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
userService.getClass().getInterfaces(),
(proxy, method, args) -> {
//開啓事務
aspect.before();
//方法返回值:業務方法的返回值
Object retObj = method.invoke(userService, args);
//System.out.println("攔截返回值:" + retObj);
//提交事務
aspect.after();
return retObj;
}
);
return serviceProxy;
}
}
測試函數
public class AOPTest {
public static void main(String[] args) {
UserService userService = MyBeanFactory.createUserService();
userService.deleteUser(1);
userService.addUser();
userService.updateUser();
}
}
測試結果如下所示:
開啓事務
通過id刪除用戶
提交事務
<=====================>
開啓事務
添加用戶
提交事務
<=====================>
開啓事務
更新用戶
提交事務
<=====================>
從測試結果可以看出,每次在執行業務代碼的時候,會攔截對應的方法,執行切面類。
重點:JDK 的動態代理是通過接口和實現類完成的。
在 debug 測試函數: UserService userService = MyBeanFactory.createUserService();
可以看出直接得到的是代理對象,代理對象中就有實現的功能。
在 debug 測試函數:UserService userService = new UserServiceImpl();
可以看出直接得到的是其實現類,沒有代理。
感興趣的同學可以將測試函數中代碼替換成上面兩種。看看測試結果,以便於更好理解 AOP 思想。
3、AOP 中通過 CGlib 動態代理簡單案例演示
public class StudentService {
void delete() {
System.out.println("刪除用戶");
}
void add() {
System.out.println("添加用戶");
}
void update() {
System.out.println("更新用戶");
}
}
public class MyBeanFactory {
// CGlib實現代理
public static StudentService createStudentService(){
//1.創建目標對象target
final StudentService studentService = new StudentService();
//2.聲明切面類對象
final MyAspect aspect = new MyAspect();
//3.創建增強對象
Enhancer enhancer = new Enhancer();
//設置父類
enhancer.setSuperclass(studentService.getClass());
//設置回調【攔截】
enhancer.setCallback((MethodInterceptor) (proxy, method, args, methodProxy) -> {
aspect.before();
Object retObj = methodProxy.invokeSuper(proxy,args);//解藕
System.out.println("攔截.....");
aspect.after();
return retObj;
});
//創建代理對象
StudentService serviceProxy = (StudentService) enhancer.create();
//System.out.println("serviceProxy);
return serviceProxy;
}
}
測試函數
public class AOPTest {
public static void main(String[] args) {
StudentService studentService = MyBeanFactory.createStudentService();
studentService.add();
System.out.println("<=====================>");
studentService.delete();
System.out.println("<=====================>");
studentService.update();
System.out.println("<=====================>");
}
}
測試結果如下所示:
開啓事務
添加用戶
攔截.....
提交事務
<=====================>
開啓事務
刪除用戶
攔截.....
提交事務
<=====================>
開啓事務
更新用戶
攔截.....
提交事務
<=====================>
CGlib 代理重點總結
a、沒有接口,只有實現類。
b、採用字節碼增強框架 cglib,在運行時 創建目標類的子類,從而對目標類進行增強。