使用Spring開啟注解AOP的支持放置的位置
Spring開啟注解AOP的支持放置的位置
放在springmvc的aop,需要在springmvc的配置文件中寫開啟aop,而不是spring的配置文件
最近使用aop來(lái)記錄controller的日志.
但是發(fā)現(xiàn)沒有起作用.
后來(lái)發(fā)現(xiàn)是因?yàn)槲业?aop 是寫在controller層(在springmvc的容器中 -web中)
而開啟aop配置卻寫在spring(applicationContext.xml)中
所以不起作用,需要在springmvc.xml的配置文件中也開啟才行
Spring AOP注解配置
啟動(dòng)AOP配置
要在 Spring 應(yīng)用中使用 AspectJ 注解,需要如下支持:
1. 在 classpath 下包含 AspectJ 類庫(kù):aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
2. 將 aop Schema 添加到 Bean 配置文件 <beans> 根元素中。
3. 在 Bean 配置文件中定義一個(gè)空的 XML 元素 <aop:aspectj-autoproxy><aop:aspectj-autoproxy/>
注:當(dāng) Spring IOC 容器偵測(cè)到 Bean 配置文件中的<aop:aspectj-autoproxy><aop:aspectj-autoproxy/> 元素時(shí),會(huì)自動(dòng)為與 AspectJ 切面匹配的 Bean 創(chuàng)建代理。
用 AspectJ 注解聲明切面
要在 Spring 中聲明 AspectJ 切面,只需要在 IOC 容器中將切面聲明為 Bean 實(shí)例。當(dāng)在 Spring IOC 容器中初始化 AspectJ 切面之后,Spring IOC 容器就會(huì)為那些與 AspectJ 切面相匹配的 Bean 創(chuàng)建代理。
在 AspectJ 注解中,切面只是一個(gè)帶有 @Aspect 注解的 Java 類。通知是標(biāo)注有某種注解的簡(jiǎn)單的 Java 方法。
AspectJ 支持 5 種類型的通知注解:
@Before:前置通知,在方法執(zhí)行之前執(zhí)行@After:后置通知,在方法執(zhí)行之后執(zhí)行@AfterRunning:返回通知,在方法返回結(jié)果之后執(zhí)行@AfterThrowing:異常通知,在方法拋出異常之后@Around:環(huán)繞通知,圍繞著方法執(zhí)行
前置通知
前置通知:在方法執(zhí)行之前執(zhí)行的通知。
前置通知使用 @Before 注解,并將切入點(diǎn)表達(dá)式的值作為注解值。
/**
* 把這個(gè)類聲明為一個(gè)切面:
* 1. 使用注解“@Repository”把該類放入到IOC容器中
* 2. 使用注解“@Aspect”把該類聲明為一個(gè)切面
*
* 設(shè)置切面的優(yōu)先級(jí):
* 3. 使用注解“@Order(number)”指定前面的優(yōu)先級(jí),值越小,優(yōu)先級(jí)越高
*/
@Order(1)
@Aspect
@Repository
public class DaoLogAspect {
/**
* 聲明該方法是一個(gè)前置通知:在目標(biāo)方法開始之前執(zhí)行
* '@Before'標(biāo)識(shí)這個(gè)方法是個(gè)前置通知,切點(diǎn)表達(dá)式表示執(zhí)行 UserDao類的 insert(User user) 方法.
*/
@Before("execution(public xyz.huning.spring4.aop.dao.User xyz.huning.spring4.aop.dao.impl.UserDao.insert(xyz.huning.spring4.aop.dao.User))")
public void beforeInsert()
{
System.out.println("--beforeInsert------------");
}
}
利用方法簽名編寫 AspectJ 切入點(diǎn)表達(dá)式
最典型的切入點(diǎn)表達(dá)式是根據(jù)方法的簽名來(lái)匹配各種方法:
execution * xyz.huning.spring4.aop.dao.impl.UserDao.*(..):匹配UserDao中聲明的所有方法,第一個(gè) * 代表任意修飾符及任意返回值。第二個(gè) * 代表任意方法。 .. 匹配任意數(shù)量的參數(shù)。若目標(biāo)類與接口與該切面在同一個(gè)包中,可以省略包名。execution public * UserDao.*(..):匹配 UserDao類的所有公有方法。execution public double UserDao.*(..):匹配 UserDao中返回 double 類型數(shù)值的方法。execution public double UserDao.*(double, ..):匹配第一個(gè)參數(shù)為 double 類型的方法, .. 匹配任意數(shù)量任意類型的參數(shù)。execution public double UserDao.*(double,double):匹配參數(shù)類型為 double,double 類型的方法。
合并切入點(diǎn)表達(dá)式
在 AspectJ 中,切入點(diǎn)表達(dá)式可以通過操作符 &&,||,! 結(jié)合起來(lái)。
/**
* 合并切入點(diǎn)表達(dá)式
* 在 AspectJ 中,切入點(diǎn)表達(dá)式可以通過操作符 &&,||,! 結(jié)合起來(lái)。
*/
@Pointcut("execution(* *.insert(..)) || execution(* *.delete(..))")
public void insertDeleteJoinPoint(){}
讓通知訪問當(dāng)前連接點(diǎn)的細(xì)節(jié)
可以在通知方法中聲明一個(gè)類型為 JoinPoint 的參數(shù)。然后就能訪問鏈接細(xì)節(jié)。如方法名稱和參數(shù)值。
/**
* 聲明該方法是一個(gè)前置通知:在目標(biāo)方法開始之前執(zhí)行
*/
@Before("execution(public xyz.huning.spring4.aop.dao.User xyz.huning.spring4.aop.dao.impl.UserDao.insert(xyz.huning.spring4.aop.dao.User))")
public void beforeInsert(JoinPoint joinPoint)
{
System.out.println("--beforeInsert with joinPoint------------");
//獲取方法名稱
String methodName = joinPoint.getSignature().getName();
//獲取參數(shù)值
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Taget method: " + methodName);
System.out.println("Taget method args: " + args);
}
后置通知
后置通知是在連接點(diǎn)完成之后執(zhí)行的, 即連接點(diǎn)返回結(jié)果或者拋出異常的時(shí)候,下面的后置通知記錄了方法的終止。
一個(gè)切面可以包括一個(gè)或者多個(gè)通知。
/**
* 聲明該方法是一個(gè)后置通知:在目標(biāo)方法開始之后執(zhí)行(即使目標(biāo)方法執(zhí)行出現(xiàn)異常也會(huì)執(zhí)行)
* 后置通知中不能訪問目標(biāo)方法的執(zhí)行結(jié)果
*/
@After("execution(public xyz.huning.spring4.aop.dao.User xyz.huning.spring4.aop.dao.impl.UserDao.insert(xyz.huning.spring4.aop.dao.User))")
public void afterInsert()
{
System.out.println("--afterInsert------------");
}
返回通知
無(wú)論連接點(diǎn)是正常返回還是拋出異常,后置通知都會(huì)執(zhí)行。如果只想在連接點(diǎn)返回的時(shí)候記錄日志,應(yīng)使用返回通知代替后置通知。在返回通知中,只要將 returning 屬性添加到 @AfterReturning 注解中,就可以訪問連接點(diǎn)的返回值。該屬性的值即為用來(lái)傳入返回值的參數(shù)名稱。
必須在通知方法的簽名中添加一個(gè)同名參數(shù)。在運(yùn)行時(shí),Spring AOP 會(huì)通過這個(gè)參數(shù)傳遞返回值。原始的切點(diǎn)表達(dá)式需要出現(xiàn)在 pointcut 屬性中。
/**
* 聲明該方法是一個(gè)返回通知:在目標(biāo)方法正常結(jié)束之后返回(目標(biāo)方法執(zhí)行出現(xiàn)異常時(shí)不再執(zhí)行)
* 返回通知可以訪問目標(biāo)方法的執(zhí)行結(jié)果
*/
@AfterReturning(value="execution(* xyz.huning.spring4.aop.dao.impl.UserDao.query(..))",returning="result")
public void afterQueryReturning(JoinPoint joinPoint,Object result)
{
System.out.println("--afterQueryReturning with joinPoint and result------------");
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Taget method: " + methodName);
System.out.println("Taget method args: " + args);
System.out.println("Taget method execute result: " + result);
}
異常通知
只在連接點(diǎn)拋出異常時(shí)才執(zhí)行異常通知,將 throwing 屬性添加到 @AfterThrowing 注解中,也可以訪問連接點(diǎn)拋出的異常。Throwable 是所有錯(cuò)誤和異常類的超類。
所以在異常通知方法可以捕獲到任何錯(cuò)誤和異常。如果只對(duì)某種特殊的異常類型感興趣,可以將參數(shù)聲明為其他異常的參數(shù)類型。然后通知就只在拋出這個(gè)類型及其子類的異常時(shí)才被執(zhí)行。
/**
* 聲明該方法是一個(gè)異常通知:在目標(biāo)方法出現(xiàn)異常時(shí)執(zhí)行此方法
* 異常通知可以訪問目標(biāo)方法中的異常對(duì)象,且可以指定在出現(xiàn)特定異常時(shí)再執(zhí)行通知代碼
*/
@AfterThrowing(value="execution(* xyz.huning.spring4.aop.dao.impl.UserDao.*(..))",throwing="e")
public void afterAllThrowing(JoinPoint joinPoint,Exception e)
{
System.out.println("--afterAllThrowing with throwing------------");
System.out.println("Taget method execute exception: " + e);
}
/**
* 指定在出現(xiàn)特定異常時(shí)再執(zhí)行通知代碼
*/
@AfterThrowing(value="execution(* xyz.huning.spring4.aop.dao.impl.UserDao.*(..))",throwing="e")
public void afterAllThrowing(JoinPoint joinPoint,NullPointerException e)
{
System.out.println("--afterAllThrowing with NullPointerException------------");
System.out.println("Taget method execute exception: " + e);
}
環(huán)繞通知
環(huán)繞通知是所有通知類型中功能最為強(qiáng)大的,能夠全面地控制連接點(diǎn)。甚至可以控制是否執(zhí)行連接點(diǎn)。對(duì)于環(huán)繞通知來(lái)說,連接點(diǎn)的參數(shù)類型必須是 ProceedingJoinPoint 。
它是 JoinPoint 的子接口,允許控制何時(shí)執(zhí)行,是否執(zhí)行連接點(diǎn)。在環(huán)繞通知中需要明確調(diào)用 ProceedingJoinPoint 的 proceed() 方法來(lái)執(zhí)行被代理的方法。如果忘記這樣做就會(huì)導(dǎo)致通知被執(zhí)行了,但目標(biāo)方法沒有被執(zhí)行。
注意:環(huán)繞通知的方法需要返回目標(biāo)方法執(zhí)行之后的結(jié)果,即調(diào)用 joinPoint.proceed()的返回值,否則會(huì)出現(xiàn)空指針異常。
/**
* 環(huán)繞通知需要攜帶ProceedingJoinPoint類型的參數(shù)
* 環(huán)繞通知類似于動(dòng)態(tài)代理的全過程:ProceedingJoinPoint類型的參數(shù)可以決定是否執(zhí)行目標(biāo)方法
* 且環(huán)繞通知必須有返回值,返回值即為目標(biāo)方法的返回值
*/
@Around("execution(* xyz.huning.spring4.aop.dao.impl.UserDao.delete(..))")
public Object around(ProceedingJoinPoint pj)
{
Object result = null;
String methodName = pj.getSignature().getName();
try{
//前置通知
System.out.println("The method: " + methodName + "前置通知");
//執(zhí)行目標(biāo)方法
result = pj.proceed();
//返回通知
System.out.println("The method: " + methodName + "返回通知");
}catch(Throwable e)
{
//異常通知
System.out.println("The method: " + methodName + "異常通知: " + e.getMessage());
}
//后置通知
System.out.println("The method: " + methodName + "后置通知");
return result;
}
指定切面的優(yōu)先級(jí)
在同一個(gè)連接點(diǎn)上應(yīng)用不止一個(gè)切面時(shí),除非明確指定,否則它們的優(yōu)先級(jí)是不確定的。
切面的優(yōu)先級(jí)可以通過實(shí)現(xiàn) Ordered 接口或利用 @Order 注解指定。實(shí)現(xiàn) Ordered 接口,,getOrder() 方法的返回值越小,優(yōu)先級(jí)越高。若使用 @Order 注解,序號(hào)出現(xiàn)在注解中。
重用切入點(diǎn)定義
在編寫 AspectJ 切面時(shí),可以直接在通知注解中書寫切入點(diǎn)表達(dá)式,但同一個(gè)切點(diǎn)表達(dá)式可能會(huì)在多個(gè)通知中重復(fù)出現(xiàn)。
在 AspectJ 切面中,可以通過 @Pointcut 注解將一個(gè)切入點(diǎn)聲明成簡(jiǎn)單的方法。切入點(diǎn)的方法體通常是空的,因?yàn)閷⑶腥朦c(diǎn)定義與應(yīng)用程序邏輯混在一起是不合理的。
切入點(diǎn)方法的訪問控制符同時(shí)也控制著這個(gè)切入點(diǎn)的可見性。如果切入點(diǎn)要在多個(gè)切面中共用,最好將它們集中在一個(gè)公共的類中。在這種情況下,它們必須被聲明為 public。
在引入這個(gè)切入點(diǎn)時(shí),必須將類名也包括在內(nèi)。如果類沒有與這個(gè)切面放在同一個(gè)包中,還必須包含包名。其他通知可以通過方法名稱引入該切入點(diǎn)。
/**
*
* ************重用切點(diǎn)表達(dá)式********************************************************************************
*
* 定義一個(gè)方法,用于聲明切入點(diǎn)表達(dá)式,一般的,該方法中不再不要填土其他的代碼。
* 使用@Pointcut來(lái)聲明切入點(diǎn)表達(dá)式。
* 同一個(gè)類中其他通知直接使用方法名來(lái)引用當(dāng)前的切入點(diǎn)表達(dá)式,如:@Before("method()")
* 同一個(gè)報(bào)下其他類中的通知需要在方法名前加類名,如:@Before("class.method()")
* 其他包下面類中的通知需要在方法名前加類的全額限定名,如:@AfterReturning(value="package.class.method()",returning="result")
*
* 第一個(gè)星號(hào)代表匹配任意修飾符及任意返回值, 第二個(gè)星號(hào)表示任意方法名稱,參數(shù)列表中的兩個(gè)點(diǎn)號(hào)表示任意數(shù)量和類型的參數(shù)
*/
@Pointcut("execution(* xyz.huning.spring4.aop.dao.impl.UserDao.*(..))")
public void userDaoJoinPoint(){}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatis動(dòng)態(tài)SQL如何實(shí)現(xiàn)前端指定返回字段
這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL如何實(shí)現(xiàn)前端指定返回字段,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
淺談Java中實(shí)現(xiàn)深拷貝的兩種方式—clone() & Serialized
這篇文章主要介紹了Java中實(shí)現(xiàn)深拷貝的兩種方式—clone() & Serialized,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
利用Spring boot如何創(chuàng)建簡(jiǎn)單的web交互應(yīng)用
這篇文章主要介紹了利用Spring boot如何創(chuàng)建簡(jiǎn)單的web交互應(yīng)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-04-04
Java 根據(jù)某個(gè) key 加鎖的實(shí)現(xiàn)方式
日常開發(fā)中,有時(shí)候需要根據(jù)某個(gè) key 加鎖,確保多線程情況下,對(duì)該 key 的加鎖和解鎖之間的代碼串行執(zhí)行,這篇文章主要介紹了Java 根據(jù)某個(gè) key 加鎖的實(shí)現(xiàn)方式,需要的朋友可以參考下2023-03-03
SpringCloud的網(wǎng)關(guān)Zuul和Gateway詳解
SpringCloudZuul和SpringCloudGateway都是用于構(gòu)建微服務(wù)架構(gòu)中的API網(wǎng)關(guān)的組件,但SpringCloudGateway在性能、功能特性和生態(tài)支持等方面有一些優(yōu)勢(shì),因此推薦使用SpringCloudGateway作為首選2025-02-02
Springboot使用Logback實(shí)現(xiàn)日志配置與異常記錄
默認(rèn)情況下,SpringBoot內(nèi)部使用logback作為系統(tǒng)日志實(shí)現(xiàn)的框架,將日志輸出到控制臺(tái),不會(huì)寫到日志文件。本篇文章主要講解下如何自定義logabck.xml以及對(duì)logback文件中配置做一個(gè)詳解,需要的可以參考一下2022-11-11
Spring boot啟動(dòng)原理及相關(guān)組件整理
這篇文章主要介紹了Spring boot啟動(dòng)原理及相關(guān)組件整理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-12-12

