一、簡(jiǎn)介
成都創(chuàng)新互聯(lián)公司于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站設(shè)計(jì)、網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元霞山做網(wǎng)站,已為上家服務(wù),為霞山各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
AOP主要包含了通知、切點(diǎn)和連接點(diǎn)等術(shù)語(yǔ),介紹如下:
通知(advice)
通知定義了切面是什么以及何時(shí)調(diào)用,何時(shí)調(diào)用包含以下幾種
切點(diǎn)(PointCut)
通知定義了切面的什么和何時(shí),切點(diǎn)定義了何處,切點(diǎn)的定義會(huì)匹配通知所要織入的一個(gè)或多個(gè)連接點(diǎn),
我們通常使用明確的類的方法名稱來(lái)指定這些切點(diǎn),或是利用正則表達(dá)式定義匹配的類和方法名稱來(lái)指定這些切點(diǎn)。
連接點(diǎn)(JoinPoint)
連接點(diǎn)是在應(yīng)用執(zhí)行過(guò)程中能夠插入切面的一個(gè)點(diǎn),這個(gè)點(diǎn)可以是調(diào)用方法時(shí),拋出異常時(shí),甚至是修改一個(gè)字段時(shí),
切面代碼可以利用這些連接點(diǎn)插入到應(yīng)用的正常流程中,并添加新的行為,如日志、安全、事務(wù)、緩存等。
joinPoint.getSignature().getDeclaringTypeName()
可以獲取到調(diào)用方法的類名(包括包名),joinPoint.getSignature().getName()
可以獲取方法名,Arrays.toString(joinPoint.getArgs())
得到的是方法調(diào)用的參數(shù)列表,joinPoint.proceed()
可以得到方法的返回結(jié)果二、注解開(kāi)發(fā)
聲明一個(gè)切面,只需要在類名上添加@Aspect屬性即可,具體的連接點(diǎn),我們用@Pointcut和@Before、@After等標(biāo)注。
在聲明前 我們需要依賴配置pom
org.aspectj aspectjrt 1.6.11 org.aspectj aspectjweaver 1.6.11
例子
package com.ganji.demo.service.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Service; /** * Created by admin on 2015/9/2. */ @Aspect @Service public class XmlAopDemoUserLog { // 配置切點(diǎn) 及要傳的參數(shù) @Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)") public void pointCut(int id) { } // 配置連接點(diǎn) 方法開(kāi)始執(zhí)行時(shí)通知 @Before("pointCut(id)") public void beforeLog(int id) { System.out.println("開(kāi)始執(zhí)行前置通知 日志記錄:"+id); } // 方法執(zhí)行完后通知 @After("pointCut(id)") public void afterLog(int id) { System.out.println("開(kāi)始執(zhí)行后置通知 日志記錄:"+id); } // 執(zhí)行成功后通知 @AfterReturning("pointCut(id)") public void afterReturningLog(int id) { System.out.println("方法成功執(zhí)行后通知 日志記錄:"+id); } // 拋出異常后通知 @AfterThrowing("pointCut(id)") public void afterThrowingLog(int id) { System.out.println("方法拋出異常后執(zhí)行通知 日志記錄"+id); } // 環(huán)繞通知 @Around("pointCut(id)") public Object aroundLog(ProceedingJoinPoint joinpoint,int id) { Object result = null; try { System.out.println("環(huán)繞通知開(kāi)始 日志記錄"+id); long start = System.currentTimeMillis(); //有返回參數(shù) 則需返回值 result = joinpoint.proceed(); long end = System.currentTimeMillis(); System.out.println("總共執(zhí)行時(shí)長(zhǎng)" + (end - start) + " 毫秒"); System.out.println("環(huán)繞通知結(jié)束 日志記錄"); } catch (Throwable t) { System.out.println("出現(xiàn)錯(cuò)誤"); } return result; } }
AOP切面中的同步問(wèn)題
在WebLogAspect切面中,分別通過(guò)doBefore和doAfterReturning兩個(gè)獨(dú)立函數(shù)實(shí)現(xiàn)了切點(diǎn)頭部和切點(diǎn)返回后執(zhí)行的內(nèi)容,
若我們想統(tǒng)計(jì)請(qǐng)求的處理時(shí)間,就需要在doBefore處記錄時(shí)間,并在doAfterReturning處通過(guò)當(dāng)前時(shí)間與開(kāi)始處記錄的時(shí)間計(jì)算
得到請(qǐng)求處理的消耗時(shí)間。
那么我們是否可以在WebLogAspect切面中定義一個(gè)成員變量來(lái)給doBefore和doAfterReturning一起訪問(wèn)呢?是否會(huì)有同步問(wèn)題呢?
的確,直接在這里定義基本類型會(huì)有同步問(wèn)題,所以我們可以引入ThreadLocal對(duì)象,像下面這樣進(jìn)行記錄:
@Aspect @Component public class WebLogAspect { private Logger logger = Logger.getLogger(getClass()); ThreadLocalstartTime = new ThreadLocal<>(); @Pointcut("execution(public * com.juzi.web..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); // 省略日志記錄內(nèi)容 } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請(qǐng)求,返回內(nèi)容 logger.info("RESPONSE : " + ret); logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); } }
AOP切面的優(yōu)先級(jí)
由于通過(guò)AOP實(shí)現(xiàn),程序得到了很好的解耦,但是也會(huì)帶來(lái)一些問(wèn)題,比如:我們可能會(huì)對(duì)Web層做多個(gè)切面,校驗(yàn)用戶,
校驗(yàn)頭信息等等,這個(gè)時(shí)候經(jīng)常會(huì)碰到切面的處理順序問(wèn)題。
所以,我們需要定義每個(gè)切面的優(yōu)先級(jí),我們需要@Order(i)注解來(lái)標(biāo)識(shí)切面的優(yōu)先級(jí)。i的值越小,優(yōu)先級(jí)越高。
假設(shè)我們還有一個(gè)切面是CheckNameAspect用來(lái)校驗(yàn)name必須為didi,我們?yōu)槠湓O(shè)置@Order(10),
而上文中WebLogAspect設(shè)置為@Order(5),所以WebLogAspect有更高的優(yōu)先級(jí),這個(gè)時(shí)候執(zhí)行順序是這樣的:
在@Before中優(yōu)先執(zhí)行@Order(5)的內(nèi)容,再執(zhí)行@Order(10)的內(nèi)容
在@After和@AfterReturning中優(yōu)先執(zhí)行@Order(10)的內(nèi)容,再執(zhí)行@Order(5)的內(nèi)容
所以我們可以這樣子總結(jié):
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。