spring中AOP 注解開發(fā)示例詳解
一、簡介
AOP主要包含了通知、切點和連接點等術(shù)語,介紹如下:
通知(advice)
通知定義了切面是什么以及何時調(diào)用,何時調(diào)用包含以下幾種
- Before 在方法被調(diào)用之前調(diào)用通知
- After 在方法完成之后調(diào)用通知,無論方法執(zhí)行是否成功
- After-returning 在方法成功執(zhí)行之后調(diào)用通知
- After-throwing 在方法拋出異常后調(diào)用通知
- Around 通知包裹了被通知的方法,在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為
切點(PointCut)
通知定義了切面的什么和何時,切點定義了何處,切點的定義會匹配通知所要織入的一個或多個連接點,
我們通常使用明確的類的方法名稱來指定這些切點,或是利用正則表達(dá)式定義匹配的類和方法名稱來指定這些切點。
連接點(JoinPoint)
連接點是在應(yīng)用執(zhí)行過程中能夠插入切面的一個點,這個點可以是調(diào)用方法時,拋出異常時,甚至是修改一個字段時,
切面代碼可以利用這些連接點插入到應(yīng)用的正常流程中,并添加新的行為,如日志、安全、事務(wù)、緩存等。
joinPoint.getSignature().getDeclaringTypeName()可以獲取到調(diào)用方法的類名(包括包名),joinPoint.getSignature().getName()可以獲取方法名,Arrays.toString(joinPoint.getArgs())得到的是方法調(diào)用的參數(shù)列表,joinPoint.proceed()可以得到方法的返回結(jié)果
二、注解開發(fā)
聲明一個切面,只需要在類名上添加@Aspect屬性即可,具體的連接點,我們用@Pointcut和@Before、@After等標(biāo)注。
在聲明前 我們需要依賴配置pom
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.11</version> </dependency>
例子
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 {
// 配置切點 及要傳的參數(shù)
@Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)")
public void pointCut(int id)
{
}
// 配置連接點 方法開始執(zhí)行時通知
@Before("pointCut(id)")
public void beforeLog(int id) {
System.out.println("開始執(zhí)行前置通知 日志記錄:"+id);
}
// 方法執(zhí)行完后通知
@After("pointCut(id)")
public void afterLog(int id) {
System.out.println("開始執(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)繞通知開始 日志記錄"+id);
long start = System.currentTimeMillis();
//有返回參數(shù) 則需返回值
result = joinpoint.proceed();
long end = System.currentTimeMillis();
System.out.println("總共執(zhí)行時長" + (end - start) + " 毫秒");
System.out.println("環(huán)繞通知結(jié)束 日志記錄");
} catch (Throwable t) {
System.out.println("出現(xiàn)錯誤");
}
return result;
}
}
AOP切面中的同步問題
在WebLogAspect切面中,分別通過doBefore和doAfterReturning兩個獨立函數(shù)實現(xiàn)了切點頭部和切點返回后執(zhí)行的內(nèi)容,
若我們想統(tǒng)計請求的處理時間,就需要在doBefore處記錄時間,并在doAfterReturning處通過當(dāng)前時間與開始處記錄的時間計算
得到請求處理的消耗時間。
那么我們是否可以在WebLogAspect切面中定義一個成員變量來給doBefore和doAfterReturning一起訪問呢?是否會有同步問題呢?
的確,直接在這里定義基本類型會有同步問題,所以我們可以引入ThreadLocal對象,像下面這樣進(jìn)行記錄:
@Aspect
@Component
public class WebLogAspect {
private Logger logger = Logger.getLogger(getClass());
ThreadLocal<Long> startTime = 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 {
// 處理完請求,返回內(nèi)容
logger.info("RESPONSE : " + ret);
logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
}
}
AOP切面的優(yōu)先級
由于通過AOP實現(xiàn),程序得到了很好的解耦,但是也會帶來一些問題,比如:我們可能會對Web層做多個切面,校驗用戶,
校驗頭信息等等,這個時候經(jīng)常會碰到切面的處理順序問題。
所以,我們需要定義每個切面的優(yōu)先級,我們需要@Order(i)注解來標(biāo)識切面的優(yōu)先級。i的值越小,優(yōu)先級越高。
假設(shè)我們還有一個切面是CheckNameAspect用來校驗name必須為didi,我們?yōu)槠湓O(shè)置@Order(10),
而上文中WebLogAspect設(shè)置為@Order(5),所以WebLogAspect有更高的優(yōu)先級,這個時候執(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é):
- 在切入點前的操作,按order的值由小到大執(zhí)行
- 在切入點后的操作,按order的值由大到小執(zhí)行
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
詳解spring+springmvc+mybatis整合注解
本篇文章主要介紹了詳解spring+springmvc+mybatis整合注解,詳細(xì)的介紹了ssm框架的使用,具有一定的參考價值,有興趣的可以了解一下2017-04-04
spring boot里增加表單驗證hibernate-validator并在freemarker模板里顯示錯誤信息(推
這篇文章主要介紹了spring boot里增加表單驗證hibernate-validator并在freemarker模板里顯示錯誤信息的相關(guān)資料,需要的朋友可以參考下2018-01-01
深入理解Java虛擬機(jī)_動力節(jié)點Java學(xué)院整理
虛擬機(jī)是一種抽象化的計算機(jī),通過在實際的計算機(jī)上模擬各種計算機(jī)功能來實現(xiàn)的,下面通過本文給大家分享Java虛擬機(jī)相關(guān)知識,感興趣的朋友一起看看吧2017-06-06

