基于spring AOP @Around @Before @After的區(qū)別說明
此段小代碼演示了spring aop中@Around @Before @After三個注解的區(qū)別
@Before是在所攔截方法執(zhí)行之前執(zhí)行一段邏輯。
@After 是在所攔截方法執(zhí)行之后執(zhí)行一段邏輯。
@Around是可以同時在所攔截方法的前后執(zhí)行一段邏輯。
連接點(JoinPoint) 這個就更好解釋了,就是spring允許你是通知(Advice)的地方,那可就真多了,基本每個方法的前、后(兩者都有也行),或拋出異常是時都可以是連接點,spring只支持方法連接點。
其他如AspectJ還可以讓你在構(gòu)造器或?qū)傩宰⑷霑r都行,不過那不是咱們關(guān)注的,只要記住,和方法有關(guān)的前前后后都是連接點。
package com.itsoft.action; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.stereotype.Controller; /** * * @author zxf * 演示aop測試類 */ @Controller public class UserAction { public void queryUsers(){ System.out.println("查詢所有用戶【all users list】"); } public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("application-aop.xml"); UserAction userAction = (UserAction)ctx.getBean("userAction"); userAction.queryUsers(); ctx.destroy(); } }
package com.itsoft; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * * @author Administrator * 通過aop攔截后執(zhí)行具體操作 */ @Aspect @Component public class LogIntercept { @Pointcut("execution(public * com.itsoft.action..*.*(..))") public void recordLog(){} @Before("recordLog()") public void before() { this.printLog("已經(jīng)記錄下操作日志@Before 方法執(zhí)行前"); } @Around("recordLog()") public void around(ProceedingJoinPoint pjp) throws Throwable{ this.printLog("已經(jīng)記錄下操作日志@Around 方法執(zhí)行前"); pjp.proceed(); this.printLog("已經(jīng)記錄下操作日志@Around 方法執(zhí)行后"); } @After("recordLog()") public void after() { this.printLog("已經(jīng)記錄下操作日志@After 方法執(zhí)行后"); } private void printLog(String str){ System.out.println(str); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:annotation-config /> <context:component-scan base-package="com.itsoft"/> <aop:aspectj-autoproxy /> </beans>
補充:spring aop的@Before,@Around,@After,@AfterReturn,@AfterThrowing的理解
1.AOP的基本概念
切面(Aspect) :通知(advice)和切入點(pointcut)共同組成了切面(aspect),時間、地點和要發(fā)生的“故事”。
可以從注解方式來理解,代碼如下。
@aspect為類上面的注解——切面
@pointcut(…)——切入點。為此類內(nèi)一個空方法上面的注解。可以把攔截的地址表達式表示為方法簽名,利于使用起來方便。
@before@after等——通知。為此類下面的方法上面的注解。
三者在一塊組成一個切面。
@Aspect public class ExampleAspect { @Pointcut("execution(* com.psjay.example.spring.aop.*.*(..))") public void aPointcut() { } @Before("aPointcut()") public void beforeAdvice() { System.out.println("before advice is executed!"); } }
連接點(Joinpoint) :程序能夠應用通知的一個“時機”,這些“時機”就是連接點,例如方法被調(diào)用時、異常被拋出時等等?!梢岳斫鉃楸籥op攔截的類或者方法就是連接點。
通知(Advice) :通知定義了切面是什么以及何時使用。描述了切面要完成的工作和何時需要執(zhí)行這個工作?!梢岳斫鉃楸蛔⒔庥蠤Before等advice注解的安全校驗的方法,攔截了過來的請求要做什么邏輯的校驗。
切入點(Pointcut) :通知定義了切面要發(fā)生的“故事”和時間,那么切入點就定義了“故事”發(fā)生的地點,例如某個類或方法的名稱。——可以理解為切面切向哪里?是個類或者某層的包路徑。
目標對象(Target Object) :即被通知的對象。
AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態(tài)代理和CGLIB代理。默認情況下,TargetObject實現(xiàn)了接口時,則采用JDK動態(tài)代理;反之,采用CGLIB代理。
織入(Weaving)把切面應用到目標對象來創(chuàng)建新的代理對象的過程,織入一般發(fā)生在如下幾個時機:
(1)編譯時:當一個類文件被編譯時進行織入,這需要特殊的編譯器才能做到,例如AspectJ的織入編譯器;
(2)類加載時:使用特殊的ClassLoader在目標類被加載到程序之前增強類的字節(jié)代碼;
(3)運行時:切面在運行的某個時刻被織入,SpringAOP就是以這種方式織入切面的,原理是使用了JDK的動態(tài)代理。
2 通知(Advice)類型的說明
@Before
前置通知(Before advice) :在某連接點(JoinPoint)——核心代碼(類或者方法)之前執(zhí)行的通知,但這個通知不能阻止連接點前的執(zhí)行。
為啥不能阻止線程進入核心代碼呢?
因為@Before注解的方法入?yún)⒉荒軅鱌roceedingJoinPoint,而只能傳入JoinPoint。
要知道從aop走到核心代碼就是通過調(diào)用ProceedingJionPoint的proceed()方法。
而JoinPoint沒有這個方法。
這里牽扯區(qū)別這兩個類:Proceedingjoinpoint 繼承了 JoinPoint 。
是在JoinPoint的基礎(chǔ)上暴露出 proceed 這個方法。proceed很重要,這個是aop代理鏈執(zhí)行的方法。
暴露出這個方法,就能支持 aop:around 這種切面(而其他的幾種切面只需要用到JoinPoint,這跟切面類型有關(guān)), 能決定是否走代理鏈還是走自己攔截的其他邏輯。
建議看一下 JdkDynamicAopProxy的invoke方法,了解一下代理鏈的執(zhí)行原理。
這樣你就能明白 proceed方法的重要性。
@After
后通知(After advice) :當某連接點退出的時候執(zhí)行的通知(不論是正常返回還是異常退出)。
@AfterReturning
返回后通知(After return advice) :在某連接點正常完成后執(zhí)行的通知,不包括拋出異常的情況。
@Around
環(huán)繞通知(Around advice) :包圍一個連接點的通知,類似Web中Servlet規(guī)范中的Filter的doFilter方法。
可以在方法的調(diào)用前后完成自定義的行為,也可以選擇不執(zhí)行。
這時aop的最重要的,最常用的注解。
用這個注解的方法入?yún)鞯氖荘roceedingJionPoint pjp,可以決定當前線程能否進入核心方法中——通過調(diào)用pjp.proceed();
@AfterThrowing
拋出異常后通知(After throwing advice) : 在方法拋出異常退出時執(zhí)行的通知。
3 advice(通知)注解的執(zhí)行先后順序
這里說下簡單情況——針對一個方法只被一個aspect類攔截時,aspect類內(nèi)部的 advice 將按照以下的順序進行執(zhí)行情況如下:
解釋:執(zhí)行到核心業(yè)務方法或者類時,會先執(zhí)行AOP。在aop的邏輯內(nèi),先走@Around注解的方法。
然后是@Before注解的方法,然后這兩個都通過了,走核心代碼,核心代碼走完,無論核心有沒有返回值,都會走@After方法。
然后如果程序無異常,正常返回就走@AfterReturn,有異常就走@AfterThrowing。
復雜的同一個方法被多個Aspect類攔截請參看博文:Spring AOP @Before @Around @After 等 advice 的執(zhí)行順序。
4 在aop中校驗不通過如何不讓程序進入核心代碼?
通過aop中注解的執(zhí)行的先后順序我們知道,校驗發(fā)生在核心代碼前面的只剩下兩個——@Before,@Around。
@Before :
這個注解只有在異常時才不會走核心方法——連接點。正常@Before無法阻止當前線程進入連接點。
@Around :
這個注解在連接點前后執(zhí)行。并且注解的方法傳入的ProceedingJionPoint 類中封裝的代理方法proceed()可以讓當前線程從aop方法轉(zhuǎn)到連接點——核心代碼方法。
所以一般我們用這個注解,如果aop的安全校驗不通過,則不調(diào)用proceed()方法,就永遠不會進入連接點。
除此外,要注意除了Around注解的方法可以傳ProceedingJionPoint 外,別的幾個都不能傳這個類。
但是普通的數(shù)據(jù)類型是不限制的。
注解的方法的返回值也不限制,可以自由限制。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Mybatis不支持batchInsertOrUpdate返顯id問題
這篇文章主要介紹了Mybatis不支持batchInsertOrUpdate返顯id問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05springboot如何讀取自定義properties并注入到bean中
這篇文章主要介紹了springboot讀取自定義properties并注入到bean中,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11springboot log4j2不能打印框架錯誤日志的解決方案
這篇文章主要介紹了springboot log4j2不能打印框架錯誤日志的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08springboot2整合redis使用lettuce連接池的方法(解決lettuce連接池無效問題)
這篇文章主要介紹了springboot2整合redis使用lettuce連接池(解決lettuce連接池無效問題),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12