Java中切面的使用方法舉例詳解
前言
在Java中,切面編程(Aspect-Oriented Programming, AOP)是一種編程范式,用于將橫切關(guān)注點(diǎn)(如日志記錄、事務(wù)管理、安全性等)從業(yè)務(wù)邏輯中分離出來。AOP通過將這些關(guān)注點(diǎn)模塊化為“切面”,使代碼更易于維護(hù)和擴(kuò)展。
一、概念和原理
基本概念
切面(Aspect):一個(gè)切面是一個(gè)模塊化的關(guān)注點(diǎn)集合,它包含了多個(gè)通知和切入點(diǎn)的定義。例如,日志切面會(huì)定義在哪些方法執(zhí)行前后進(jìn)行日志記錄。
通知(Advice):通知定義了在切入點(diǎn)執(zhí)行時(shí)要執(zhí)行的代碼邏輯,也就是在特定的連接點(diǎn)上執(zhí)行的操作。常見的通知類型有前置通知、后置通知、環(huán)繞通知等。
切入點(diǎn)(Pointcut):切入點(diǎn)用于定義哪些連接點(diǎn)(程序執(zhí)行過程中的特定位置,如方法調(diào)用、異常拋出等)會(huì)觸發(fā)通知的執(zhí)行。它可以通過方法名、類名、參數(shù)類型等條件進(jìn)行精確匹配。
連接點(diǎn)(Join point):連接點(diǎn)是程序執(zhí)行過程中可以插入切面的點(diǎn),例如方法調(diào)用、字段訪問等。在 Java 中,主要的連接點(diǎn)通常是方法調(diào)用。
AOP 實(shí)現(xiàn)方式及原理
1.基于代理的 AOP 實(shí)現(xiàn)(如 Spring AOP)
原理:Spring AOP 默認(rèn)使用代理模式來實(shí)現(xiàn) AOP 功能,有兩種代理方式:JDK 動(dòng)態(tài)代理和 CGLIB 代理。
JDK 動(dòng)態(tài)代理:當(dāng)目標(biāo)對象實(shí)現(xiàn)了接口時(shí),Spring AOP 會(huì)使用 JDK 動(dòng)態(tài)代理。它基于 Java 的 java.lang.reflect.Proxy 類和 java.lang.reflect.InvocationHandler 接口。在運(yùn)行時(shí),JDK 動(dòng)態(tài)代理會(huì)根據(jù)目標(biāo)對象實(shí)現(xiàn)的接口動(dòng)態(tài)生成一個(gè)代理類,該代理類實(shí)現(xiàn)了與目標(biāo)對象相同的接口。當(dāng)調(diào)用代理對象的方法時(shí),會(huì)觸發(fā) InvocationHandler 的 invoke() 方法,在該方法中可以插入切面邏輯。
CGLIB 代理:當(dāng)目標(biāo)對象沒有實(shí)現(xiàn)接口時(shí),Spring AOP 會(huì)使用 CGLIB 代理。CGLIB(Code Generation Library)是一個(gè)強(qiáng)大的、高性能的代碼生成庫,它通過繼承目標(biāo)對象的類來創(chuàng)建代理對象。在運(yùn)行時(shí),CGLIB 會(huì)動(dòng)態(tài)生成一個(gè)目標(biāo)對象的子類,并重寫目標(biāo)對象的方法,在重寫的方法中插入切面邏輯。
代碼如下:
import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.framework.ProxyFactory; import java.lang.reflect.Method; // 定義業(yè)務(wù)接口 interface BusinessService { void doBusiness(); } // 實(shí)現(xiàn)業(yè)務(wù)接口 class BusinessServiceImpl implements BusinessService { @Override public void doBusiness() { System.out.println("執(zhí)行核心業(yè)務(wù)邏輯"); } } // 定義前置通知 class MyBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("前置通知:在方法執(zhí)行前記錄日志"); } } public class AOPProxyExample { public static void main(String[] args) { // 創(chuàng)建目標(biāo)對象 BusinessService target = new BusinessServiceImpl(); // 創(chuàng)建前置通知對象 MyBeforeAdvice advice = new MyBeforeAdvice(); // 創(chuàng)建代理工廠 ProxyFactory proxyFactory = new ProxyFactory(); // 設(shè)置目標(biāo)對象 proxyFactory.setTarget(target); // 添加通知 proxyFactory.addAdvice(advice); // 獲取代理對象 BusinessService proxy = (BusinessService) proxyFactory.getProxy(); // 調(diào)用代理對象的方法 proxy.doBusiness(); } }
二、基于字節(jié)碼增強(qiáng)的 AOP 實(shí)現(xiàn)(如 AspectJ)
原理:AspectJ 是一個(gè)功能強(qiáng)大的 AOP 框架,它采用字節(jié)碼增強(qiáng)的方式來實(shí)現(xiàn) AOP。在編譯時(shí)或類加載時(shí),AspectJ 會(huì)修改目標(biāo)類的字節(jié)碼,將切面邏輯直接插入到目標(biāo)類的方法中。與基于代理的實(shí)現(xiàn)方式不同,字節(jié)碼增強(qiáng)不需要?jiǎng)?chuàng)建代理對象,而是直接在目標(biāo)類的字節(jié)碼中添加切面代碼,因此性能更高,并且可以處理更多的連接點(diǎn)。
編譯時(shí)織入:在編譯 Java 源代碼時(shí),AspectJ 編譯器(ajc)會(huì)對源代碼進(jìn)行處理,將切面邏輯織入到目標(biāo)類的字節(jié)碼中。這種方式需要使用 AspectJ 編譯器來編譯代碼。
類加載時(shí)織入:在類加載時(shí),通過 Java 的類加載器對目標(biāo)類的字節(jié)碼進(jìn)行修改,將切面邏輯織入其中。這種方式不需要使用 AspectJ 編譯器,只需要在運(yùn)行時(shí)配置類加載器即可。
代碼如下:
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; // 定義切面類 @Aspect public class LoggingAspect { // 定義切入點(diǎn) @Pointcut("execution(* com.example.BusinessService.*(..))") public void businessServiceMethods() {} // 定義前置通知 @Before("businessServiceMethods()") public void beforeBusinessServiceMethod(JoinPoint joinPoint) { System.out.println("前置通知:在方法執(zhí)行前記錄日志,方法名:" + joinPoint.getSignature().getName()); } } // 定義業(yè)務(wù)類 class BusinessService { public void doBusiness() { System.out.println("執(zhí)行核心業(yè)務(wù)邏輯"); } } public class AspectJExample { public static void main(String[] args) { BusinessService service = new BusinessService(); service.doBusiness(); } }
總結(jié)
到此這篇關(guān)于Java中切面的使用方法的文章就介紹到這了,更多相關(guān)JAVA中切面使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring框架通過工廠創(chuàng)建Bean的三種方式實(shí)現(xiàn)
這篇文章主要介紹了Spring框架通過工廠創(chuàng)建Bean的三種方式實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03解決javaBean規(guī)范導(dǎo)致json傳參首字母大寫將永遠(yuǎn)獲取不到問題
這篇文章主要介紹了解決javaBean規(guī)范導(dǎo)致json傳參首字母大寫將永遠(yuǎn)獲取不到問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07logback標(biāo)記日志過濾器MarkerFilter源碼解讀
這篇文章主要為大家介紹了logback標(biāo)記日志過濾器MarkerFilter源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Spring?Boot自定義?Starter并推送到遠(yuǎn)端公服的詳細(xì)代碼
這篇文章主要介紹了Spring?Boot自定義?Starter并推送到遠(yuǎn)端公服,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09談?wù)凷pring AOP中@Aspect的高級(jí)用法示例
在Spring AOP中目前只有執(zhí)行方法這一個(gè)連接點(diǎn),下面這篇文章主要給大家介紹了關(guān)于Spring AOP中@Aspect的高級(jí)用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08Java中調(diào)用Python的實(shí)現(xiàn)示例
本文主要介紹了Java中調(diào)用Python的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Java中如何利用Set判斷List集合中是否有重復(fù)元素
在開發(fā)工作中,我們有時(shí)需要去判斷List集合中是否含有重復(fù)的元素,這時(shí)候我們不需要找出重復(fù)的元素,我們只需要返回一個(gè)?Boolean?類型就可以了,下面通過本文給大家介紹Java中利用Set判斷List集合中是否有重復(fù)元素,需要的朋友可以參考下2023-05-05Java8 使用 stream().sorted()對List集合進(jìn)行排序的操作
這篇文章主要介紹了Java8 使用 stream().sorted()對List集合進(jìn)行排序的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10