什么是AOP
Spring是java面向?qū)ο缶幊痰闹髁骺蚣埽渲饕枷胗袃蓚€(gè):反轉(zhuǎn)控制(IOC)和面向切面(AOP)。
AOP(Aspect Oriented Programming)稱為面向切面編程,是對(duì)面向?qū)ο螅∣OP)的補(bǔ)充和完善袍祖。
普通的面向?qū)ο蠡诜庋b、繼承、多態(tài)等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu)宾尚,但僅限于縱向?qū)哟危瑹o(wú)法做到橫向關(guān)聯(lián)。而日志煌贴、異常處理等功能涉及各個(gè)類御板,代碼橫向地散布在所有層次中,傳統(tǒng)OOP難以復(fù)用這些代碼牛郑。
而AOP利用一種稱為"橫切"的技術(shù)怠肋,剖解開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊淹朋,并將其命名為"Aspect"笙各,即切面。
所謂"切面"础芍,簡(jiǎn)單說(shuō)就是那些與業(yè)務(wù)無(wú)關(guān)杈抢,卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼仑性,降低模塊之間的耦合度惶楼,并有利于未來(lái)的可操作性和可維護(hù)性。
日志中統(tǒng)計(jì)執(zhí)行時(shí)間
對(duì)應(yīng)代碼中的關(guān)鍵方法虏缸,在日志中通過(guò)計(jì)時(shí)統(tǒng)計(jì)執(zhí)行時(shí)間鲫懒,可以監(jiān)控功能執(zhí)行是否正常,也可以觀察程序的性能刽辙,作為優(yōu)化效果的判斷依據(jù)窥岩。
統(tǒng)計(jì)執(zhí)行時(shí)間可以使用Stopwatch工具類,引入
import com.google.common.base.Stopwatch;
使用createStarted()開(kāi)始計(jì)時(shí)宰缤,結(jié)束時(shí)用elapsed統(tǒng)計(jì)耗時(shí)颂翼。
代碼示例如下:
import com.google.common.base.Stopwatch;
Stopwatch stopwatch = Stopwatch.createStarted();
/**
* 功能代碼
*/
log.info("xxxxx,elapsed:{} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS));
不過(guò)這種方式只能統(tǒng)計(jì)一個(gè)功能的耗時(shí),若現(xiàn)在有100個(gè)接口都需要統(tǒng)計(jì)耗時(shí)的話慨灭,顯然是沒(méi)辦法每個(gè)都去這么寫(xiě)的朦乏。
這時(shí)就需要配合AOP技術(shù)來(lái)實(shí)現(xiàn)了。
使用AOP構(gòu)建通用計(jì)時(shí)類
先放完整的實(shí)現(xiàn):
@Component
@Aspect
@Log4j
public class TimingUtil {
@Pointcut("execution(* com.xxx.xx.controller..*.*(..))")
private void pointCut(){}
@Around("pointCut()")
public Object timing(ProceedingJoinPoint joinPoint){
Object object;
Stopwatch stopwatch = Stopwatch.createStarted();
try{
object = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
log.info("API request completed, takes " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms.");
return object;
}
}
來(lái)看這里面的關(guān)鍵點(diǎn):
1 Aspect和Component注解
Component注解對(duì)于Springboot來(lái)說(shuō)相當(dāng)于聲明bean并添加至xml配置中氧骤,加了Component的類編譯時(shí)就被視為Springboot項(xiàng)目的bean呻疹,進(jìn)行拼裝。
Aspect注解則聲明了該類是一個(gè)切面筹陵。
2 PointCut注解
該注解用來(lái)聲明AOP切面的切入點(diǎn)刽锤,或者說(shuō),告訴程序什么時(shí)候要使用切面介入朦佩。
execution表示在執(zhí)行某些方法時(shí)介入并思,是PointCut最常用的介入方式。
表達(dá)式格式為:(返回類型语稠,方法路徑.方法名(參數(shù)類型))
在本例中宋彼,要介入的是接口類以獲取時(shí)間,第一個(gè)* 代表不限制返回類型,接口類存放的路徑在com.xxx.xx.controller中输涕,后面..* 表示controller包下的所有類音婶。
再往后.*表示這個(gè)類下的所有方法,(..)代表不限制傳入的參數(shù)類型占贫。
3 Around注解
在捕獲切入點(diǎn)后桃熄,AOP類就可以進(jìn)行切面上的操作了先口。
根據(jù)需要處理的行為方式不同型奥,可以使用不同的注解標(biāo)記,例如前置處理@Before碉京、后置處理@AfterRunning厢汹、異常處理@AfterThrowing、環(huán)繞處理@Around等等谐宙。
這里我們要記錄接口執(zhí)行的時(shí)間烫葬,因此需要在接口調(diào)用前開(kāi)始計(jì)時(shí),在接口返回后停止計(jì)時(shí)凡蜻,所以需要環(huán)繞處理搭综。
需要在Around注解后標(biāo)記切入方法名,因?yàn)榭赡懿恢挂粋€(gè)切入划栓。
使用Around注解時(shí)兑巾,切面方法必須使用入?yún)roceedingJoinPoint,代表切入點(diǎn)忠荞,切面方法返回類型默認(rèn)選object(當(dāng)然蒋歌,如果可以確保所有返回類型一致,也可以直接定義)委煤。
joinPoint.proceed()這個(gè)方法表示執(zhí)行切入點(diǎn)的方法堂油,在此之前的代碼部分為前置處理,在此之后的代碼部分為后置處理碧绞。
所以在執(zhí)行proceed之前啟動(dòng)秒表府框,在執(zhí)行后記錄時(shí)間,這樣對(duì)所有接口進(jìn)行計(jì)時(shí)的功能就完成了讥邻。