Spring AOP使用@Aspect注解 面向切面實(shí)現(xiàn)日志橫切的操作
引言:
AOP為Aspect Oriented Programming的縮寫(xiě),意為:面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù).AOP是OOP的延續(xù),是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容,是函數(shù)式編程的一種衍生范型。
利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。
在Spring AOP中業(yè)務(wù)邏輯僅僅只關(guān)注業(yè)務(wù)本身,將日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來(lái),通過(guò)對(duì)這些行為的分離,我們希望可以將它們獨(dú)立到非指導(dǎo)業(yè)務(wù)邏輯的方法中,進(jìn)而改變這些行為的時(shí)候不影響業(yè)務(wù)邏輯的代碼。
相關(guān)注解介紹如下:
@Aspect
:作用是把當(dāng)前類(lèi)標(biāo)識(shí)為一個(gè)切面供容器讀取
@Pointcut
:Pointcut是植入Advice的觸發(fā)條件。每個(gè)Pointcut的定義包括2部分,一是表達(dá)式,二是方法簽名。方法簽名必須是 public及void型??梢詫ointcut中的方法看作是一個(gè)被Advice引用的助記符,因?yàn)楸磉_(dá)式不直觀,因此我們可以通過(guò)方法簽名的方式為 此表達(dá)式命名。因此Pointcut中的方法只需要方法簽名,而不需要在方法體內(nèi)編寫(xiě)實(shí)際代碼。
@Around
:環(huán)繞增強(qiáng),相當(dāng)于MethodInterceptor
@AfterReturning
:后置增強(qiáng),相當(dāng)于AfterReturningAdvice,方法正常退出時(shí)執(zhí)行
@Before
:標(biāo)識(shí)一個(gè)前置增強(qiáng)方法,相當(dāng)于BeforeAdvice的功能,相似功能的還有
@AfterThrowing
:異常拋出增強(qiáng),相當(dāng)于ThrowsAdvice
一:引入相關(guān)依賴(lài)
二:Spring的配置文件
applicationContext.xml 中引入context、aop對(duì)應(yīng)的命名空間;配置自動(dòng)掃描的包,同時(shí)使切面類(lèi)中相關(guān)方法中的注解生效,需自動(dòng)地為匹配到的方法所在的類(lèi)生成代理對(duì)象。
創(chuàng)建簡(jiǎn)單計(jì)算器的接口ArithmeticCalculator.java及實(shí)現(xiàn)類(lèi)ArithmeticCalculatorImpl.java
package com.svse.aop; public interface ArithmeticCalculator { //定義四個(gè)簡(jiǎn)單的借口 加減乘除算法 int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
package com.svse.aop; import org.springframework.stereotype.Component; //將實(shí)現(xiàn)類(lèi)加入Spring的IOC容器進(jìn)行管理 @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; } }
現(xiàn)在想在實(shí)現(xiàn)類(lèi)中的每個(gè)方法執(zhí)行前、后、以及是否發(fā)生異常等信息打印出來(lái),需要把日志信息抽取出來(lái),寫(xiě)到對(duì)應(yīng)的切面的類(lèi)中 LoggingAspect.java 中 要想把一個(gè)類(lèi)變成切面類(lèi),需要兩步,
① 在類(lèi)上使用 @Component 注解 把切面類(lèi)加入到IOC容器中
② 在類(lèi)上使用 @Aspect 注解 使之成為切面類(lèi)
package com.svse.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; import com.tencentcloudapi.vod.v20180717.VodClient; /** * 日志切面 * * @author zhaoshuiqing<br> * @date 2019年6月14日 下午3:03:29 */ @Component @Aspect public class LoggingAspect { //現(xiàn)在想在實(shí)現(xiàn)類(lèi)中的每個(gè)方法執(zhí)行前、后、以及是否發(fā)生異常等信息打印出來(lái),需要把日志信息抽取出來(lái),寫(xiě)到對(duì)應(yīng)的切面的類(lèi)中 LoggingAspect.java 中 //要想把一個(gè)類(lèi)變成切面類(lèi),需要兩步, //① 在類(lèi)上使用 @Component 注解 把切面類(lèi)加入到IOC容器中 //② 在類(lèi)上使用 @Aspect 注解 使之成為切面類(lèi) /** * 前置通知:目標(biāo)方法執(zhí)行之前執(zhí)行以下方法體的內(nèi)容 * @param jp */ @Before("execution(* com.svse.aop.*.*(..))") public void beforeMethod(JoinPoint jp){ String methodName =jp.getSignature().getName(); System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); } /** * 返回通知:目標(biāo)方法正常執(zhí)行完畢時(shí)執(zhí)行以下代碼 * @param jp * @param result */ @AfterReturning(value="execution(* com.svse.aop.*.*(..))",returning="result") public void afterReturningMethod(JoinPoint jp, Object result){ String methodName =jp.getSignature().getName(); System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】"); } /** * 后置通知:目標(biāo)方法執(zhí)行之后執(zhí)行以下方法體的內(nèi)容,不管是否發(fā)生異常。 * @param jp */ @After("execution(* com.svse.aop.*.*(..))") public void afterMethod(JoinPoint jp){ System.out.println("【后置通知】this is a afterMethod advice..."); } /** * 異常通知:目標(biāo)方法發(fā)生異常的時(shí)候執(zhí)行以下代碼 */ @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e") public void afterThorwingMethod(JoinPoint jp, NullPointerException e){ String methodName = jp.getSignature().getName(); System.out.println("【異常通知】the method 【" + methodName + "】 occurs exception: " + e); } /** * 環(huán)繞通知:目標(biāo)方法執(zhí)行前后分別執(zhí)行一些代碼,發(fā)生異常的時(shí)候執(zhí)行另外一些代碼 * @return */ /*@Around(value="execution(* com.svse.aop.*.*(..))") public Object aroundMethod(ProceedingJoinPoint jp){ String methodName = jp.getSignature().getName(); Object result = null; try { System.out.println("【環(huán)繞通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); //執(zhí)行目標(biāo)方法 result = jp.proceed(); System.out.println("【環(huán)繞通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result); } catch (Throwable e) { System.out.println("【環(huán)繞通知中的--->異常通知】:the method 【" + methodName + "】 occurs exception " + e); } System.out.println("【環(huán)繞通知中的--->后置通知】:-----------------end.----------------------"); return result; }*/ }
編寫(xiě)MainTest方法進(jìn)行測(cè)試
package com.svse.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainTest { public static void main(String[] args) { //ClassPathXmlApplicationContext默認(rèn)是加載src目錄下的xml文件 ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator =(ArithmeticCalculator) ctx.getBean("arithmeticCalculator"); System.out.println(arithmeticCalculator.getClass()); int result = arithmeticCalculator.add(3, 5); System.out.println("result: " + result); result = arithmeticCalculator.div(5, 0); System.out.println("result: " + result); } }
運(yùn)行結(jié)果:
把其它代碼都注釋掉,把環(huán)繞通知的方法釋放出來(lái),測(cè)試結(jié)果如下:
至此AOP注解面向切面記錄日志就寫(xiě)完了!
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java程序執(zhí)行過(guò)程及內(nèi)存機(jī)制詳解
本講將介紹Java代碼是如何一步步運(yùn)行起來(lái)的,還會(huì)介紹Java程序所占用的內(nèi)存是被如何管理的:堆、棧和方法區(qū)都各自負(fù)責(zé)存儲(chǔ)哪些內(nèi)容,感興趣的朋友跟隨小編一起看看吧2020-12-12mybatis中映射文件(mapper)中的使用規(guī)則
這篇文章主要介紹了mybatis中映射文件(mapper)中的使用規(guī)則,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11springboot多項(xiàng)目結(jié)構(gòu)實(shí)現(xiàn)
本文主要介紹了springboot多項(xiàng)目結(jié)構(gòu)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01springboot中nacos-client獲取配置的實(shí)現(xiàn)方法
本文主要介紹了springboot中nacos-client獲取配置的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04HashMap vs TreeMap vs Hashtable vs LinkedHashMap
這篇文章主要介紹了HashMap vs TreeMap vs Hashtable vs LinkedHashMap的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07關(guān)于break和continue以及l(fā)abel的區(qū)別和作用(詳解)
下面小編就為大家?guī)?lái)一篇關(guān)于break和continue以及l(fā)abel的區(qū)別和作用(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05Nebula?Graph介紹和SpringBoot環(huán)境連接和查詢(xún)操作
Nebula?Graph?是一款開(kāi)源的、分布式的、易擴(kuò)展的原生圖數(shù)據(jù)庫(kù),能夠承載包含數(shù)千億個(gè)點(diǎn)和數(shù)萬(wàn)億條邊的超大規(guī)模數(shù)據(jù)集,并且提供毫秒級(jí)查詢(xún),這篇文章主要介紹了Nebula?Graph介紹和SpringBoot環(huán)境連接和查詢(xún),需要的朋友可以參考下2022-10-10