Java中切面的使用方法舉例詳解
前言
在Java中,切面編程(Aspect-Oriented Programming, AOP)是一種編程范式,用于將橫切關(guān)注點(diǎn)(如日志記錄、事務(wù)管理、安全性等)從業(yè)務(wù)邏輯中分離出來(lái)。AOP通過(guò)將這些關(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í)行的操作。常見(jiàn)的通知類(lèi)型有前置通知、后置通知、環(huán)繞通知等。
切入點(diǎn)(Pointcut):切入點(diǎn)用于定義哪些連接點(diǎn)(程序執(zhí)行過(guò)程中的特定位置,如方法調(diào)用、異常拋出等)會(huì)觸發(fā)通知的執(zhí)行。它可以通過(guò)方法名、類(lèi)名、參數(shù)類(lèi)型等條件進(jìn)行精確匹配。
連接點(diǎn)(Join point):連接點(diǎn)是程序執(zhí)行過(guò)程中可以插入切面的點(diǎn),例如方法調(diào)用、字段訪問(wèn)等。在 Java 中,主要的連接點(diǎn)通常是方法調(diào)用。
AOP 實(shí)現(xiàn)方式及原理
1.基于代理的 AOP 實(shí)現(xiàn)(如 Spring AOP)
原理:Spring AOP 默認(rèn)使用代理模式來(lái)實(shí)現(xiàn) AOP 功能,有兩種代理方式:JDK 動(dòng)態(tài)代理和 CGLIB 代理。
JDK 動(dòng)態(tài)代理:當(dāng)目標(biāo)對(duì)象實(shí)現(xiàn)了接口時(shí),Spring AOP 會(huì)使用 JDK 動(dòng)態(tài)代理。它基于 Java 的 java.lang.reflect.Proxy 類(lèi)和 java.lang.reflect.InvocationHandler 接口。在運(yùn)行時(shí),JDK 動(dòng)態(tài)代理會(huì)根據(jù)目標(biāo)對(duì)象實(shí)現(xiàn)的接口動(dòng)態(tài)生成一個(gè)代理類(lèi),該代理類(lèi)實(shí)現(xiàn)了與目標(biāo)對(duì)象相同的接口。當(dāng)調(diào)用代理對(duì)象的方法時(shí),會(huì)觸發(fā) InvocationHandler 的 invoke() 方法,在該方法中可以插入切面邏輯。
CGLIB 代理:當(dāng)目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口時(shí),Spring AOP 會(huì)使用 CGLIB 代理。CGLIB(Code Generation Library)是一個(gè)強(qiáng)大的、高性能的代碼生成庫(kù),它通過(guò)繼承目標(biāo)對(duì)象的類(lèi)來(lái)創(chuàng)建代理對(duì)象。在運(yùn)行時(shí),CGLIB 會(huì)動(dòng)態(tài)生成一個(gè)目標(biāo)對(duì)象的子類(lèi),并重寫(xiě)目標(biāo)對(duì)象的方法,在重寫(xiě)的方法中插入切面邏輯。
代碼如下:
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)對(duì)象
BusinessService target = new BusinessServiceImpl();
// 創(chuàng)建前置通知對(duì)象
MyBeforeAdvice advice = new MyBeforeAdvice();
// 創(chuàng)建代理工廠
ProxyFactory proxyFactory = new ProxyFactory();
// 設(shè)置目標(biāo)對(duì)象
proxyFactory.setTarget(target);
// 添加通知
proxyFactory.addAdvice(advice);
// 獲取代理對(duì)象
BusinessService proxy = (BusinessService) proxyFactory.getProxy();
// 調(diào)用代理對(duì)象的方法
proxy.doBusiness();
}
}
二、基于字節(jié)碼增強(qiáng)的 AOP 實(shí)現(xiàn)(如 AspectJ)
原理:AspectJ 是一個(gè)功能強(qiáng)大的 AOP 框架,它采用字節(jié)碼增強(qiáng)的方式來(lái)實(shí)現(xiàn) AOP。在編譯時(shí)或類(lèi)加載時(shí),AspectJ 會(huì)修改目標(biāo)類(lèi)的字節(jié)碼,將切面邏輯直接插入到目標(biāo)類(lèi)的方法中。與基于代理的實(shí)現(xiàn)方式不同,字節(jié)碼增強(qiáng)不需要?jiǎng)?chuàng)建代理對(duì)象,而是直接在目標(biāo)類(lèi)的字節(jié)碼中添加切面代碼,因此性能更高,并且可以處理更多的連接點(diǎn)。
編譯時(shí)織入:在編譯 Java 源代碼時(shí),AspectJ 編譯器(ajc)會(huì)對(duì)源代碼進(jìn)行處理,將切面邏輯織入到目標(biāo)類(lèi)的字節(jié)碼中。這種方式需要使用 AspectJ 編譯器來(lái)編譯代碼。
類(lèi)加載時(shí)織入:在類(lèi)加載時(shí),通過(guò) Java 的類(lèi)加載器對(duì)目標(biāo)類(lèi)的字節(jié)碼進(jìn)行修改,將切面邏輯織入其中。這種方式不需要使用 AspectJ 編譯器,只需要在運(yùn)行時(shí)配置類(lèi)加載器即可。
代碼如下:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
// 定義切面類(lèi)
@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ù)類(lèi)
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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringSecurity解決POST方式下CSRF問(wèn)題
本文主要介紹了SpringSecurity解決POST方式下CSRF問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
解析Hibernate + MySQL中文亂碼問(wèn)題
如果持久化的類(lèi)中有包括了漢字的String對(duì)象,那么對(duì)應(yīng)到數(shù)據(jù)庫(kù)中漢字的部分就會(huì)是亂碼。這主要是由于MySQL數(shù)據(jù)表的字符集與我們當(dāng)前使用的本地字符集不相同造成的2013-07-07
java類(lèi)中生成jfreechart,返回圖表的url地址 代碼分享
這篇文章介紹了java類(lèi)中生成jfreechart,返回圖表的url地址的代碼,有需要的朋友可以參考一下2013-08-08
源碼解析springbatch的job運(yùn)行機(jī)制
這篇文章主要介紹了springbatch的job是如何運(yùn)行的,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
springboot docker jenkins 自動(dòng)化部署并上傳鏡像的步驟詳解
這篇文章主要介紹了springboot docker jenkins 自動(dòng)化部署并上傳鏡像的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
微信支付之公眾號(hào)支付(java實(shí)現(xiàn))
這篇文章主要介紹了微信支付之公眾號(hào)支付(java實(shí)現(xiàn)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
javax.validation在Spring?Boot請(qǐng)求中使用方式
在Spring?Boot中進(jìn)行請(qǐng)求參數(shù)驗(yàn)證,可以通過(guò)引入`spring-boot-starter-validation`依賴,并在實(shí)體類(lèi)和控制器中使用`javax.validation`注解來(lái)實(shí)現(xiàn),配置包括引入依賴、在實(shí)體類(lèi)中添加注解、在控制器方法中使用`@Valid`注解以及捕獲異常并返回自定義錯(cuò)誤消息2025-11-11

