您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Springboot中切面編程AOP是怎樣的,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法。它和我們平時接觸到的OOP都是編程的不同思想,OOP,即『面向對象編程』,它提倡的是將功能模塊化,對象化,而AOP的思想,則不太一樣,它提倡的是針對同一類問題的統一處理,當然,我們在實際編程過程中,不可能單純的按照AOP或者OOP的思想來編程,很多時候,可能會混合多種編程思想,大家也不必要糾結該使用哪種思想,取百家之長,才是正道。
AspectJ實際上是對AOP編程思想的一個實踐,當然,除了AspectJ以外,還有很多其它的AOP實現,例如ASMDex,但目前最好、最方便的,依然是AspectJ。
SpringAOP包含AspectJ,這是因為Spring AOP借助了AspectJ的語法,但底層技術用的還是Spring自己的。
Spring Aop采用的動態織入,而Aspectj是靜態織入。
靜態織入:指在編譯時期就織入,即:編譯出來的class文件,字節碼就已經被織入了。
動態織入又分靜動兩種,靜則指織入過程只在第一次調用時執行;動則指根據代碼動態運行的中間狀態來決定如何操作,每次調用Target的時候都執行。有不清楚的同學,可以自己補下基礎的代理知識。
pointcut切入點
通常一個pointcut,會選取程序中的某些我們感興趣的執行點,或者說是程序執行點的集合。 是一個(組)基于正則表達式的表達式。
joinPoint連接點
通過pointcut選取出來的集合中的具體的一個執行點,我們就叫JoinPoint.
target目標類
被aspectj橫切的對象。我們所說的joinPoint就是Target的某一行,如方法開始執行的地方、方法類調用某個其他方法的代碼。
advice通知
在選取出來的JoinPoint上要執行的操作、邏輯。 典型的 Advice 類型有 before、after 和 around,分別表示在目標方法執行之前、執行后和完全替代目標方法執行的代碼。 @Before前置通知 目標方法執行前 @After后置通知 目標方法執行后,無論是否發生異常 @AfterReturning后置通知 目標方法正常執行結束后,可以知道目標方法的返回值 @AfterThrowing后置通知 目標方法執行發生異常后,可以訪問異常對象,進行打印。也可以指定異常發生時才執行 @Around環繞通知 包含上面4種
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
在完成了引入AOP依賴包后,不需要去做其他配置。AOP的默認配置屬性中,spring.aop.auto屬性默認是開啟的,也就是說只要引入了AOP依賴后,默認已經增加了@EnableAspectJAutoProxy,不需要在程序主類中增加@EnableAspectJAutoProxy來啟用。
@RestController @RequestMapping("/aopController") public class AopController { @GetMapping(value = "/Curry") public void Curry(){ System.out.println("庫里上場打球了!!"); } @GetMapping(value = "/Harden") public void Harden(){ System.out.println("哈登上場打球了!!"); } @GetMapping(value = "/Antetokounmpo") public void Antetokounmpo(){ System.out.println("字母哥上場打球了!!"); } @GetMapping(value = "/Jokic") public void Jokic(){ System.out.println("約基奇上場打球了!!"); } @GetMapping(value = "/Durant/{point}") public void Durant(@PathVariable("point") int point){ System.out.println("杜蘭特上場打球了!!"); } }
@Aspect 注解 使之成為切面類
@Component 注解 把切面類加入到IOC容器中
@Aspect @Component public class CustomAspect { //定義切入點 @Pointcut("execution(public * com.lee.springaop.controller.AopController.*(..)))") public void CustomAspect(){ } @Pointcut("execution(public * com.lee.springaop.controller.AopController.Durant(..))") public void CustomAroundAspect(){ } @Pointcut("execution(public * com.lee.springaop.controller.AopController.Durant(int)) && args(int))") public void CustomAroundAspect2(){ } //前置通知 @Before("CustomAspect()") public void doBeforeGame(){ System.out.println("before : 經紀人正在處理球星賽前事務!"); } //后置通知 @After("CustomAspect()") public void doAfterGame(){ System.out.println("after : 經紀人為球星表現瘋狂鼓掌!"); } //后置通知--正常返回 @AfterReturning("CustomAspect()") public void doAfterReturningGame(){ System.out.println("after returning : 返回通知:經紀人為球星表現瘋狂鼓掌!"); } //后置通知--發生異常 @AfterThrowing("CustomAspect()") public void doAfterThrowingGame(){ System.out.println("after throwing : 異常通知:球迷要求退票!"); } //環繞通知 @Around("CustomAroundAspect()") public void doAroundGame(ProceedingJoinPoint joinPoint) { try { System.out.println("Around before 1: 經紀人正在處理球星賽前事務"); //參數 Object[] args = joinPoint.getArgs(); for (Object o : args){ System.out.println("params 1: "+o.toString()); } //target Object target = joinPoint.getTarget(); System.out.println("target 1: "+target.toString()); //進行下一個advice或目標方法調用 joinPoint.proceed(); System.out.println("Around after 1 : 返回通知:經紀人為球星表現瘋狂鼓掌"); } catch (Throwable throwable) { System.out.println("Aroung throwing 1: 異常通知:球迷要求退票"); } } //環繞通知2 @Around("CustomAroundAspect2() && args(point)") public void doAroundGame2(ProceedingJoinPoint joinPoint,int point) { try { System.out.println("Around before 2: 經紀人正在處理球星賽前事務"); //進行下一個advice或目標方法調用 joinPoint.proceed(); System.out.println("球星本場得到 2 :" + point + "分" ); System.out.println("Around after 2 : 返回通知:經紀人為球星表現瘋狂鼓掌"); } catch (Throwable throwable) { System.out.println("Aroung throwing 2 : 異常通知:球迷要求退票"); } } }
瀏覽器訪問:http://127.0.0.1:8080/aopController/Harden
瀏覽器訪問:http://127.0.0.1:8080/aopController/Durant/80
* 匹配任何數量的字符 .. 匹配任何數量字符的重復 如:類型模式中:匹配任何數量的子包; 方法參數模式中:匹配任何數量參數 + 匹配指定類型的子類型 注:僅能作為后綴放在類型模式后邊
eg:
java.lang.String 匹配String類型。 java.*.String 匹配java包下的任何“一級子包”下的String類型。匹配java.lang.String,但不匹配java.lang.ss.String java..* 匹配java包及任何子包下的任何類型。如匹配java.lang.String、java.lang.annotation.Annotation java.lang.*ing 匹配任何java.lang包下的以ing結尾的類型。 java.lang.Number+ 匹配java.lang包下的任何Number的自類型。如匹配java.lang.Integer,也匹配java.math.BigInteger “()”表示方法沒有任何參數 “(..)”表示匹配接受任意個參數的方法 “(..,java.lang.String)”表示匹配接受java.lang.String類型的參數結束,且其前邊可以接受有任意個參數的方法; “(java.lang.String,..)” 表示匹配接受java.lang.String類型的參數開始,且其后邊可以接受任意個參數的方法; “(*,java.lang.String)” 表示匹配接受java.lang.String類型的參數結束,且其前邊接受有一個任意類型參數的方法;
&& 且 || 或 ! 非
execution 匹配方法執行的鏈接點 within 用于匹配指定類型內的方法執行 this 用于匹配當前AOP代理對象類型的執行方法 target 用于匹配當前目標對象類型的執行方法 args 用于匹配當前執行的方法傳入的參數為指定類型的執行方法 @within 用于匹配所以持有指定注解類型內的方法 @target 用于匹配當前目標對象類型的執行方法,其中目標對象持有指定的注解 @args 用于匹配當前執行的方法傳入的參數持有指定注解的執行 @annotation 用于匹配當前執行方法持有指定注解的方法 bean 用于匹配特定名稱的Bean對象的執行方法 reference pointcut 表示引用其他命名切入點
模式 | 描述 |
---|---|
public * *(..) | 任何公共方法的執行 |
* cn.javass..IPointcutService.*() | cn.javass包及所有子包下IPointcutService接口中的任何無參方法 |
* cn.javass..*. *(..) | cn.javass包及所有子包下任何類的任何方法 |
* cn.javass..IPointcutService.*( *) | cn.javass包及所有子包下IPointcutService接口的任何只有一個參數方法 |
* (!cn.javass..IPointcutService+).*(..) | 非“cn.javass包及所有子包下IPointcutService接口及子類型”的任何方法 |
* cn.javass..IPointcutService+.*() | cn.javass包及所有子包下IPointcutService接口及子類型的的任何無參方法 |
* cn.javass..IPointcut*.test*(java.util.Date) | cn.javass包及所有子包下IPointcut前綴類型的的以test開頭的只有一個參數類型為java.util.Date的方法 |
* cn.javass..IPointcut*.test*(..) throws IllegalArgumentException, ArrayIndexOutOfBoundsException | cn.javass包及所有子包下IPointcut前綴類型的的任何方法,且拋出IllegalArgumentException和ArrayIndexOutOfBoundsException異常 |
* (cn.javass..IPointcutService+&& java.io.Serializable+).*(..) | 任何實現了cn.javass包及所有子包下IPointcutService接口和java.io.Serializable接口的類型的任何方法 |
@java.lang.Deprecated * *(..) | 任何持有@java.lang.Deprecated注解的方法 |
@java.lang.Deprecated @cn.javass..Secure * *(..) | 任何持有@java.lang.Deprecated和@cn.javass..Secure注解的方法 |
@(java.lang.Deprecated || cn.javass..Secure) * *(..) | 任何持有@java.lang.Deprecated或@ cn.javass..Secure注解的方法 |
(@cn.javass..Secure *) *(..) | 任何返回值類型持有@cn.javass..Secure的方法 |
* (@cn.javass..Secure ).(..) | 任何定義方法的類型持有@cn.javass..Secure的方法 |
* (@cn.javass..Secure () , @cn.javass..Secure (*)) | 任何簽名帶有兩個參數的方法,且這個兩個參數都被@ Secure標記了,如public void test(@Secure String str1, @Secure String str1); |
* *((@ cn.javass..Secure ))或 *(@ cn.javass..Secure *) | 任何帶有一個參數的方法,且該參數類型持有@ cn.javass..Secure;如public void test(Model model);且Model類上持有@Secure注解 |
* *(@cn.javass..Secure (@cn.javass..Secure *) ,@ cn.javass..Secure (@cn.javass..Secure *)) | 任何帶有兩個參數的方法,且這兩個參數都被@ cn.javass..Secure標記了;且這兩個參數的類型上都持有@ cn.javass..Secure; |
* *(java.util.Map<cn.javass..Model, cn.javass..Model>, ..) | 任何帶有一個java.util.Map參數的方法,且該參數類型是以< cn.javass..Model, cn.javass..Model >為泛型參數;注意只匹配第一個參數為java.util.Map,不包括子類型;如public void test(HashMap<Model, Model> map, String str);將不匹配,必須使用“* *(java.util.HashMap<cn.javass..Model,cn.javass..Model>, ..)”進行匹配;而public void test(Map map, int i);也將不匹配,因為泛型參數不匹配 |
* *(java.util.Collection<@cn.javass..Secure *>) | 任何帶有一個參數(類型為java.util.Collection)的方法,且該參數類型是有一個泛型參數,該泛型參數類型上持有@cn.javass..Secure注解;如public void test(Collection<Model> collection);Model類型上持有@cn.javass..Secure |
within(類型表達式) 匹配指定類內的方法執行
模式 | 描述 |
---|---|
within(cn.javass..*) | cn.javass包及子包下的任何方法執行 |
within(cn.javass..IPointcutService+) | cn.javass包或所有子包下IPointcutService類型及子類型的任何方法 |
within(@cn.javass..Secure *) | 持有cn.javass..Secure注解的任何類型的任何方法必須是在目標對象上聲明這個注解,在接口上聲明的對它不起作用 |
this(類型全限定名)匹配當前AOP代理對象類型的執行方法
模式 | 描述 |
---|---|
this(cn.javass.spring.chapter6.service.IPointcutService) | 當前AOP對象實現了 IPointcutService接口的任何方法 |
this(cn.javass.spring.chapter6.service.IIntroductionService) | 當前AOP對象實現了 IIntroductionService接口的任何方法也可能是引入接口 |
target(類型全限定名)匹配當前目標對象類型的執行方法
模式 | 描述 |
---|---|
target(cn.javass.spring.chapter6.service.IPointcutService) | 當前目標對象(非AOP對象)實現了 IPointcutService接口的任何方法 |
target(cn.javass.spring.chapter6.service.IIntroductionService) | 當前目標對象(非AOP對象) 實現了IIntroductionService 接口的任何方法不可能是引入接口 |
args(類型參數列表) 匹配當前執行的方法傳入的參數為指定類型的執行方法
模式 | 描述 |
---|---|
args (java.io.Serializable,..) | 任何一個以接受“傳入參數類型為 java.io.Serializable” 開頭,且其后可跟任意個任意類型的參數的方法執行,args指定的參數類型是在運行時動態匹配的 |
使用“@within(注解類型)”匹配所以持有指定注解類型內的方法;注解類型也必須是全限定類型名;
模式 | 描述 |
---|---|
@within cn.javass.spring.chapter6.Secure) | 任何目標對象對應的類型持有Secure注解的類方法;必須是在目標對象上聲明這個注解,在接口上聲明的對它不起作用 |
使用“@target(注解類型)”匹配當前目標對象類型的執行方法,其中目標對象持有指定的注解;注解類型也必須是全限定類型名
模式 | 描述 |
---|---|
@target (cn.javass.spring.chapter6.Secure) | 任何目標對象持有Secure注解的類方法;必須是在目標對象上聲明這個注解,在接口上聲明的對它不起作用 |
使用“@args(注解列表)”匹配當前執行的方法傳入的參數持有指定注解的執行;注解類型也必須是全限定類型名;
模式 | 描述 |
---|---|
@args (cn.javass.spring.chapter6.Secure) | 任何一個只接受一個參數的方法,且方法運行時傳入的參數持有注解 cn.javass.spring.chapter6.Secure;動態切入點,類似于arg指示符; |
使用“@annotation(注解類型)”匹配當前執行方法持有指定注解的方法;注解類型也必須是全限定類型名;
模式 | 描述 |
---|---|
@annotation(cn.javass.spring.chapter6.Secure ) | 當前執行方法上持有注解 cn.javass.spring.chapter6.Secure將被匹配 |
使用“bean(Bean id或名字通配符)”匹配特定名稱的Bean對象的執行方法;Spring ASP擴展的,在AspectJ中無相應概念
模式 | 描述 |
---|---|
bean(*Service) | 匹配所有以Service命名(id或name)結尾的Bean |
表示引用其他命名切入點,只有@ApectJ風格支持,Schema風格不支持
package cn.javass.spring.chapter6.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; @Aspect public class ReferencePointcutAspect { @Pointcut(value="execution(* *())") public void pointcut() {} }
引用:
@Before(value = "cn.javass.spring.chapter6.aop.ReferencePointcutAspect.pointcut()") public void referencePointcutTest2(JoinPoint jp) { }
關于Springboot中切面編程AOP是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。