網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
Aop面向切面編程
文章目錄
- Aop面向切面編程
- 什么是AOP
- AOP術(shù)語(yǔ)
- Spring AOP 的使用
- 導(dǎo)入依賴
- 編寫(xiě)切面類
- 切面定義語(yǔ)法
- 小細(xì)節(jié)
- 輸出日志成功
什么是AOP
AOP
:(Aspect Oriented Programming
)面向切面編程,和OOP(Object Oriented Programming
)面向?qū)ο缶幊桃粯樱彩怯?jì)算機(jī)開(kāi)發(fā)的一種程序設(shè)計(jì)思想,與OOP將程序中的每個(gè)環(huán)節(jié)對(duì)象化,如實(shí)體類等相比,面向切面就是在不改變程序現(xiàn)有代碼的前提下,可以設(shè)置某方法運(yùn)行前或運(yùn)行后新增額外代碼的操作,減少對(duì)代碼的入侵。包括過(guò)濾器、攔截器都是一種AOP的思想,只不過(guò)Spring AOP是Spring給的,(注:AOP并不是Spring框架獨(dú)有的,而是從AspectJ框架中借鑒而來(lái))過(guò)濾器是Java給的,攔截器是SpringMVC給的。
對(duì)AOP官方文檔翻譯:
AOP
目標(biāo)是將橫切關(guān)注點(diǎn)與業(yè)務(wù)主體進(jìn)行進(jìn)一步分離,以提高程序代碼的模塊化程度。通過(guò)在現(xiàn)有代碼基礎(chǔ)上增加額外的通知(Advice
)機(jī)制,能夠?qū)Ρ宦暶鳛椤?strong>切點(diǎn)“(Pointcut
)的代碼塊進(jìn)行統(tǒng)一管理與擴(kuò)展
AOP術(shù)語(yǔ)
-
切面(
Aspect
):是一個(gè)可以加入額外代碼運(yùn)行的特定位置,一般指方法之間的調(diào)用,可以在不修改原代碼的情況下,添加新的代碼,對(duì)現(xiàn)有代碼進(jìn)行升級(jí)維護(hù)和管理 -
織入(
Weaving
):選定一個(gè)切面,利用動(dòng)態(tài)代理技術(shù),為原有的方法的持有者生成動(dòng)態(tài)對(duì)象,然后將它和切面關(guān)聯(lián),在運(yùn)行原有方法時(shí),就會(huì)按織入之后的流程運(yùn)行了-
動(dòng)態(tài)代理:
-
-
目標(biāo)對(duì)象(
Target
):需要被加強(qiáng)的業(yè)務(wù)對(duì)象 -
代理類(
Proxy
) 一個(gè)類被AOP織入通知后,就產(chǎn)生了一個(gè)代理類。 -
切點(diǎn)(
PointCut
):每個(gè)程序的連接點(diǎn)有多個(gè),如何定位到某個(gè)感興趣的連接點(diǎn),就需要通過(guò)切點(diǎn)來(lái)定位。 -
連接點(diǎn)(
Joinpoint
):程序執(zhí)行的某個(gè)特定位置,如某個(gè)方法調(diào)用前,調(diào)用后,方法拋出異常后,這些代碼中的特定點(diǎn)稱為連接點(diǎn)。簡(jiǎn)單來(lái)說(shuō),就是在哪加入你的通知- 連接點(diǎn)表示具體要攔截的方法,切點(diǎn)是定義一個(gè)范圍,而連接點(diǎn)是具體到某個(gè)方法
-
通知(
Advice
):原意為增強(qiáng),是織入到目標(biāo)類連接點(diǎn)上的一段程序代碼。分以下幾種:
- 前置通知(
before
):執(zhí)行業(yè)務(wù)代碼前做些操作,比如獲取連接對(duì)象 - 后置通知(
after
):在執(zhí)行業(yè)務(wù)代碼后做些操作,無(wú)論是否發(fā)生異常,它都會(huì)執(zhí)行,比如關(guān)閉連接對(duì)象 - 異常通知(
afterThrowing
):在執(zhí)行業(yè)務(wù)代碼后出現(xiàn)異常,需要做的操作,比如回滾事務(wù) - 返回通知(
afterReturning
):在執(zhí)行業(yè)務(wù)代碼后無(wú)異常,會(huì)執(zhí)行的操作 - 環(huán)繞通知(
around
):
- 前置通知(
Spring AOP 的使用
導(dǎo)入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
編寫(xiě)切面類
@Slf4j
@Aspect //切面
@Component
public class LogAspect {
private HashMap<String, Object> map = new HashMap<>();
private final String filePath = "logFile.txt";
//1.定義切面
/**
* 此步驟單純定義切面,就是指定一個(gè)方法
* 可以在這個(gè)方法運(yùn)行前或運(yùn)行后等位置織入額外代碼
*
* 匹配com.liner.controller包及其子包下的所有類的所有方法
*/
@Pointcut("execution(public * com.liner.controller.*.*(..))")
// 切入點(diǎn) 執(zhí)行 公有方法 任意返回值 包路徑 任何類 任何方法 任何參數(shù)
public void pointCut() {}
//2.織入內(nèi)容
/**
* 前置通知,目標(biāo)方法調(diào)用前被調(diào)用
* 向切面方法前添加代碼,在方法運(yùn)行前輸出指定內(nèi)容
*/
@Before("pointCut()")
public void beforeAdvice(JoinPoint joinPoint) { //JoinPoint 會(huì)包含當(dāng)前切面的各種信息,連接點(diǎn)
log.info("----------- 前置通知 -----------");
Signature signature = joinPoint.getSignature(); //獲得當(dāng)前切面對(duì)應(yīng)方法的信息
log.info("返回目標(biāo)方法的簽名:{}", signature);
log.info("代理的是哪一個(gè)方法:{}", signature.getName());
Object[] args = joinPoint.getArgs();
log.info("獲取目標(biāo)方法的參數(shù)信息:{}", Arrays.asList(args));
map.put("方法簽名", signature);
map.put("代理方法", signature.getName());
map.put("參數(shù)信息", Arrays.asList(args));
}
/**
* 后置通知,也叫最終通知,目標(biāo)方法執(zhí)行完之后執(zhí)行
*/
@After("pointCut()")
public void afterAdvice() {
log.info("----------- 后置通知 -----------");
}
/**
* 后置返回通知
* 如果參數(shù)中的第一個(gè)參數(shù)為JoinPoint,則第二個(gè)參數(shù)為返回值的信息
* 如果參數(shù)中的第一個(gè)參數(shù)不為JoinPoint,則第一個(gè)參數(shù)為returning中對(duì)應(yīng)的參數(shù)
* returning 只有目標(biāo)方法返回值與通知方法相應(yīng)參數(shù)類型時(shí)才能執(zhí)行后置返回通知,否則不執(zhí)行
*
* @param joinPoint
* @param keys
*/
@AfterReturning(pointcut = "pointCut()", returning = "keys")
public void afterReturningAdvice(JoinPoint joinPoint, String keys) {
log.info("~~~~~~~~~~ 后置返回通知 ~~~~~~~~~~");
log.info("后置返回通知的返回值:{}", keys);
}
/**
* 后置異常通知
* 定義一個(gè)名字,該名字用于匹配通知實(shí)現(xiàn)方法的一個(gè)參數(shù)名,當(dāng)目標(biāo)方法拋出異常返回后,將把目標(biāo)方法拋出的異常傳給通知方法;
* throwing 只有目標(biāo)方法拋出的異常與通知方法相應(yīng)參數(shù)異常類型時(shí)才能執(zhí)行后置異常通知,否則不執(zhí)行,
*
* @param e
*/
@AfterThrowing(value = "pointCut()", throwing = "e")
public void afterThrowingAdvice(Exception e) {
log.info("~~~~~~~~~~ 后置異常通知 ~~~~~~~~~~");
log.info(e.toString());
}
/**
* 環(huán)繞通知
* 環(huán)繞通知非常強(qiáng)大,可以決定目標(biāo)方法是否執(zhí)行,什么時(shí)候執(zhí)行,執(zhí)行時(shí)是否需要替換方法參數(shù),執(zhí)行完畢是否需要替換返回值。
* 環(huán)繞通知第一個(gè)參數(shù)必須寫(xiě),且是org.aspectj.lang.ProceedingJoinPoint類型 JoinPoint的子接口,擁有更多方法
* 這個(gè)方法還要有返回值,因?yàn)檎{(diào)用的切面的方法可能有返回值,環(huán)繞advice如果不返回這個(gè)值,調(diào)用者就接收不到這個(gè)值了
* @param proceedingJoinPoint
*/
@Around(value = "pointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------- 環(huán)繞通知 -----------");
log.info("環(huán)繞通知的目標(biāo)方法名:{}", proceedingJoinPoint.getSignature().getName());
map.put("目標(biāo)方法", proceedingJoinPoint.getSignature().getName());
try {
return proceedingJoinPoint.proceed(); //ProceedingJoinPoint類型的參數(shù)具有調(diào)用切面方法的功能
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
log.info("---------- 環(huán)繞通知結(jié)束 -------------");
writeLog(map);
}
return null;
}
/**
* 打印日志
* @param map
*/
void writeLog(HashMap<String, Object> map) {
try {
long currentTimeMillis = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日-hh時(shí)mm分ss秒");
Date date = new Date(currentTimeMillis);
FileWriter fw = new FileWriter(filePath,true);
fw.write("----------- 環(huán)繞通知 -----------"+ format.format(date)+ "\r\n");
fw.write("環(huán)繞通知的目標(biāo)方法名:" + (String) map.get("目標(biāo)方法") + "\r\n");
fw.write("----------- 前置通知 -----------"+ "\r\n");
fw.write("返回目標(biāo)方法的簽名:" + (String) map.get("方法簽名").toString() + "\r\n");
fw.write("代理的是哪一個(gè)方法:" + (String) map.get("代理方法") + "\r\n");
fw.write("獲取目標(biāo)方法的參數(shù)信息:" + (String) map.get("參數(shù)信息").toString() + "\r\n");
fw.write("----------- 最終通知 -----------"+ "\r\n");
fw.write("---------- 環(huán)繞通知結(jié)束 -------------"+ "\r\n");
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
切面定義語(yǔ)法
@Pointcut("execution(public * com.liner.controller.*.*(..))")
下面介紹切面定義表達(dá)式的詳細(xì)語(yǔ)法規(guī)則
語(yǔ)法模板:
execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)
帶?
的是可選屬性,不帶?
是必須寫(xiě)的
-
modifier-pattern
:訪問(wèn)修飾符(可選) -
ret-type-pattern
:返回值類型(必寫(xiě)),一般為*
-
declaring-type-pattern
:全路徑類名(可選) -
name-pattern(param-pattern)
:方法名(必寫(xiě)) -
param-pattern
:參數(shù)列表(必寫(xiě)),一般為..
-
throws-pattern
:拋出的異常類型(可選)
小細(xì)節(jié)
幾種通知圖標(biāo)不同:
-
前置通知(
before
): -
后置通知(
after
): -
異常通知(
afterThrowing
): -
返回通知(
afterReturning
): -
環(huán)繞通知(
around
):
輸出日志成功
原文鏈接:https://blog.csdn.net/weixin_65777087/article/details/131501526
- 上一篇:沒(méi)有了
- 下一篇:沒(méi)有了
相關(guān)推薦
- 2022-12-05 TensorFlow中關(guān)于tf.app.flags命令行參數(shù)解析模塊_python
- 2021-12-15 go?gin+token(JWT)驗(yàn)證實(shí)現(xiàn)登陸驗(yàn)證_Golang
- 2022-03-18 linux下修改文件權(quán)限chmod命令詳細(xì)解析_Linux
- 2022-11-04 ASP.NET?MVC獲取多級(jí)類別組合下的產(chǎn)品_實(shí)用技巧
- 2022-05-11 Feign之間的文件傳輸
- 2022-12-27 React組件間通訊傳值實(shí)現(xiàn)詳解_React
- 2022-11-30 Android?Binder?通信原理圖文詳解_Android
- 2022-05-19 Python學(xué)習(xí)之異常處理的避坑指南_python
- 欄目分類
-
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支