本文小福利 點(diǎn)我獲取阿里云優(yōu)惠券
AOP核心概念
1全蝶、橫切關(guān)注點(diǎn)
對(duì)哪些方法進(jìn)行攔截,攔截后怎么處理寺枉,這些關(guān)注點(diǎn)稱(chēng)之為橫切關(guān)注點(diǎn)
2抑淫、切面(aspect)-》(通知+切點(diǎn))
類(lèi)是對(duì)物體特征的抽象,切面就是對(duì)橫切關(guān)注點(diǎn)的抽象姥闪。
通知+切點(diǎn)
意思就是所有要被應(yīng)用到增強(qiáng)(advice)代碼的地方始苇。(包括方法的方位信息)
3、連接點(diǎn)(joinpoint)-》(被攔截的方法)
被攔截到的點(diǎn)筐喳,因?yàn)镾pring只支持方法類(lèi)型的連接點(diǎn)催式,所以在Spring中連接點(diǎn)指的就是被攔截的方法,實(shí)際上連接點(diǎn)還可以是字段或者構(gòu)造器
4避归、切入點(diǎn)(pointcut)-》(描述攔截那些方法的部分)
對(duì)連接點(diǎn)進(jìn)行攔截的定義
5荣月、通知(advice)-》(攔截后執(zhí)行自己業(yè)務(wù)邏輯的那些部分)
所謂通知指的就是指攔截到連接點(diǎn)之后要執(zhí)行的代碼,通知分為前置槐脏、后置喉童、異常、最終顿天、環(huán)繞通知五類(lèi)
這玩意也叫 增強(qiáng)
在邏輯層次上包括了我們抽取的公共邏輯和方位信息堂氯。因?yàn)镾pring只能方法級(jí)別的應(yīng)用AOP,也就是我們常見(jiàn)的before,after,after-returning,after-throwing,around五種,意思就是在方法調(diào)用前后牌废,異常時(shí)候執(zhí)行我這段公共邏輯唄咽白。
6、目標(biāo)對(duì)象
代理的目標(biāo)對(duì)象
7鸟缕、織入(weave)
將切面應(yīng)用到目標(biāo)對(duì)象并導(dǎo)致代理對(duì)象創(chuàng)建的過(guò)程晶框。
比如根據(jù)Advice中的方位信息在指定切點(diǎn)的方法前后排抬,執(zhí)行增強(qiáng)。這個(gè)過(guò)程Spring 替我們做好了授段。利用的是CGLIB動(dòng)態(tài)代理技術(shù)蹲蒲。
8、引入(introduction)
在不修改代碼的前提下侵贵,引入可以在運(yùn)行期為類(lèi)動(dòng)態(tài)地添加一些方法或字段
圖解
上面那一堆看不懂對(duì)嗎届搁? 我也不太懂。
來(lái)看張圖
通知(Advice)類(lèi)型
切面一共有五種通知
Before 某方法調(diào)用之前發(fā)出通知窍育。
前置通知(Before advice) :在某連接點(diǎn)(JoinPoint)之前執(zhí)行的通知卡睦, 但這個(gè)通知不能阻止連接點(diǎn)前的執(zhí)行。在方法調(diào)用之前發(fā)出通
@Before("execution(* com.slife.java8.aspect.AspectTest.test())")
public void beforeTest() {
System.out.println("執(zhí)行 方法 之前 調(diào)用----");
}
After 某方法完成之后發(fā)出通知
后通知(After advice) :當(dāng)某連接點(diǎn)退出的時(shí)候執(zhí)行的通知(不論是正常 返回還是異常退出)漱抓。
不考慮方法運(yùn)行的結(jié)果 表锻。在方法調(diào)用之后發(fā)出通
@After("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterTest() {
System.out.println();
System.out.println("執(zhí)行 方法 之后 調(diào)用----");
}
After-returning 將通知放置在被通知的方法成功執(zhí)行之后。
方法正常返回后乞娄,調(diào)用通知瞬逊。在方法調(diào)用后,正常退出發(fā)出通
@AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterReturningTest() {
System.out.println();
System.out.println("執(zhí)行 方法 AfterReturning 調(diào)用----");
}
After-throwing 將通知放置在被通知的方法拋出異常之后补胚。
拋出異常后通知(After throwing advice) : 在方法拋出異常退出時(shí)執(zhí)行 的通知码耐。在方法調(diào)用時(shí)追迟,異常退出發(fā)出通
@AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterThrowingTest() {
System.out.println();
System.out.println("執(zhí)行 方法 AfterThrowing 調(diào)用----");
}
Around 通知包裹在被通知的方法的周?chē)?/h2>
環(huán)繞通知(Around advice) :包圍一個(gè)連接點(diǎn)的通知溶其,類(lèi)似Web中Servlet 規(guī)范中的Filter的doFilter方法《丶洌可以在方法的調(diào)用前后完成自定義的行為瓶逃,也可以選擇不執(zhí)行。在方法調(diào)用之前和之后發(fā)出通
@Around("execution(* com.slife.java8.aspect.AspectTest.test())")
public void aroundTest() {
System.out.println();
System.out.println("執(zhí)行 方法 前后 調(diào)用----");
}
執(zhí)行結(jié)果
2017-10-27 19:51:51.605 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Last-Modified value for [/aspecttest] is: -1
執(zhí)行 方法 之前 調(diào)用----
JoinpointTest++++執(zhí)行我正常流水線(xiàn)的業(yè)務(wù)邏輯
執(zhí)行 方法 之后 調(diào)用----
執(zhí)行 方法 AfterReturning 調(diào)用----
2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : Successfully completed request
切入點(diǎn)表達(dá)式
切入點(diǎn)指示符用來(lái)指示切入點(diǎn)表達(dá)式目的廓块,在Spring AOP中目前只有執(zhí)行方法這一個(gè)連接點(diǎn)厢绝,Spring AOP支持的AspectJ切入點(diǎn)指示符如下:
args()
定制join-point去匹配那些參數(shù)為指定類(lèi)型的方法的執(zhí)行動(dòng)作。
@args()
定制join-point去匹配那些參數(shù)被指定類(lèi)型注解的方法的執(zhí)行動(dòng)作
execution()
開(kāi)始匹配在其內(nèi)部編寫(xiě)的定制
this()
定制join-pont去匹配由AOP代理的Bean引用的指定類(lèi)型的類(lèi)带猴。
target()
定制join-point去匹配特定的對(duì)象昔汉,這些對(duì)象一定是指定類(lèi)型的類(lèi)。
@target()
定制join-point去匹配特定的對(duì)象拴清,這些對(duì)象要具有的指定類(lèi)型的注解靶病。
within()
定制join-point在必須哪一個(gè)包中。
@within()
定制join-point在必須由指定注解標(biāo)注的類(lèi)中口予。
@annotation
定制連接點(diǎn)具有指定的注解娄周。
只有execution用來(lái)執(zhí)行匹配,其他標(biāo)志符都只是為了限制/定制他們所要匹配的連接點(diǎn)的位置沪停。
命名及匿名切入點(diǎn)
類(lèi)型匹配語(yǔ)法
*
:匹配任何數(shù)量字符煤辨。
..
:匹配任何數(shù)量字符的重復(fù)裳涛,如在類(lèi)型模式中匹配任何數(shù)量子包;而在方法參數(shù)模式中匹配任何數(shù)量參數(shù)众辨。
+
:匹配指定類(lèi)型的子類(lèi)型端三;僅能作為后綴放在類(lèi)型模式后邊。
例子
java.lang.String 匹配String類(lèi)型鹃彻;
java.*.String 匹配java包下的任何“一級(jí)子包”下的String類(lèi)型技肩;
如匹配java.lang.String,但不匹配java.lang.ss.String
java..* 匹配java包及任何子包下的任何類(lèi)型;
如匹配java.lang.String浮声、java.lang.annotation.Annotation
java.lang.*ing 匹配任何java.lang包下的以ing結(jié)尾的類(lèi)型虚婿;
java.lang.Number+ 匹配java.lang包下的任何Number的自類(lèi)型;
如匹配java.lang.Integer泳挥,也匹配java.math.BigInteger
詳細(xì)語(yǔ)法
注解然痊? 修飾符? 返回值類(lèi)型 類(lèi)型聲明?方法名(參數(shù)列表) 異常列表?
注解:可選屉符,方法上持有的注解剧浸,如@Deprecated;
修飾符:可選矗钟,如public唆香、protected;
返回值類(lèi)型:必填吨艇,可以是任何類(lèi)型模式躬它;“*”表示所有類(lèi)型;
類(lèi)型聲明:可選东涡,可以是任何類(lèi)型模式冯吓;
方法名:必填,可以使用“*”進(jìn)行模式匹配疮跑;
參數(shù)列表:“()”表示方法沒(méi)有任何參數(shù)组贺;“(..)”表示匹配接受任意個(gè)參數(shù)的方法,“(..,java.lang.String)”表示匹配接受java.lang.String類(lèi)型的參數(shù)結(jié)束祖娘,且其前邊可以接受有任意個(gè)參數(shù)的方法失尖;“(java.lang.String,..)” 表示匹配接受java.lang.String類(lèi)型的參數(shù)開(kāi)始,且其后邊可以接受任意個(gè)參數(shù)的方法渐苏;“(*,java.lang.String)” 表示匹配接受java.lang.String類(lèi)型的參數(shù)結(jié)束掀潮,且其前邊接受有一個(gè)任意類(lèi)型參數(shù)的方法;
異常列表:可選整以,以“throws 異常全限定名列表”聲明胧辽,異常全限定名列表如有多個(gè)以“,”分割公黑,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException邑商。
匹配Bean名稱(chēng):可以使用Bean的id或name進(jìn)行匹配摄咆,并且可使用通配符“*”;
組合切入點(diǎn)表達(dá)式
AspectJ使用 且(&&)人断、或(||)吭从、非(!)來(lái)組合切入點(diǎn)表達(dá)式恶迈。在Schema風(fēng)格下涩金,由于在XML中使用“&&”需要使用轉(zhuǎn)義字符“&&”來(lái)代替之,所以很不方便暇仲,因此Spring ASP 提供了and步做、or奈附、not來(lái)代替&&全度、||斥滤、!佑颇。
通知參數(shù)
使用JoinPoint獲榷サ簟:Spring AOP提供使用org.aspectj.lang.JoinPoint類(lèi)型獲取連接點(diǎn)數(shù)據(jù),任何通知方法的第一個(gè)參數(shù)都可以是JoinPoint(環(huán)繞通知是ProceedingJoinPoint挑胸,JoinPoint子類(lèi))痒筒,當(dāng)然第一個(gè)參數(shù)位置也可以是JoinPoint.StaticPart類(lèi)型嗜暴,這個(gè)只返回連接點(diǎn)的靜態(tài)部分。
運(yùn)用場(chǎng)景
AOP 闷沥、IOC 做為Spring 的支柱,使用場(chǎng)景非常廣泛咐容。
1舆逃、日志記錄
2、權(quán)限控制
3戳粒、事務(wù)
4、多數(shù)據(jù)源讀寫(xiě)切換
原理
動(dòng)態(tài)代理
Spring中AOP代理由Spring的IOC容器負(fù)責(zé)生成奄妨、管理苹祟,其依賴(lài)關(guān)系也由IOC容器負(fù)責(zé)管理砸抛。因此,AOP代理可以直接使用容器中的其它bean實(shí)例作為目標(biāo)景东,這種關(guān)系可由IOC容器的依賴(lài)注入提供。Spring創(chuàng)建代理的規(guī)則為:
1奔誓、默認(rèn)使用Java動(dòng)態(tài)代理來(lái)創(chuàng)建AOP代理,這樣就可以為任何接口實(shí)例創(chuàng)建代理了
2和措、當(dāng)需要代理的類(lèi)不是代理接口的時(shí)候蜕煌,Spring會(huì)切換為使用CGLIB代理臼婆,也可強(qiáng)制使用CGLIB
spring boot 項(xiàng)目中定義使用自己的aop
1幌绍、引入jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、編寫(xiě)切面
@Aspect // FOR AOP
@Order(-99) // 控制多個(gè)Aspect的執(zhí)行順序傀广,越小越先執(zhí)行
@Component
public class AdviceTest {
@Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())")
public void poincut(){
}
@Before("poincut()")
public void beforeTest() {
System.out.println("執(zhí)行 方法 之前 調(diào)用----");
}
}
這樣就完成了在spring boot 項(xiàng)目中定義使用自己的aop
注意代理模式
CGLIB動(dòng)態(tài)代理技術(shù)
有時(shí)候你會(huì)發(fā)現(xiàn) 你的配置和我一樣,但是aop沒(méi)有生效誓酒,這很有可能是SpringMVC的配置的代理模式不對(duì)贮聂。
問(wèn)題描述
方法里的xxxService對(duì)象如果使用autowared注入靠柑,無(wú)法啟動(dòng)aspect吓懈,
但是
xxxService = ctx.getBean("xxxxx")獲取,是可以啟用aspect的
原因
這個(gè)時(shí)候 xxxService 并不是注入進(jìn)來(lái)的耻警,即使有 @Autowired 注解隔嫡,這時(shí)的注解沒(méi)有任何作用。
只有 Spring 生成的對(duì)象才有 AOP 功能甘穿,因?yàn)?Spring 生成的代理對(duì)象才有 AOP 功能。
解決方法
配置spring.aop.proxy-target-class=true
不錯(cuò)的一個(gè)問(wèn)題描述已經(jīng)解決方法
文章代碼
/**
* Created by chen on 2017/10/27.
* <p>
* Email 122741482@qq.com
* <p>
* Describe:
*/
@Service
public class JoinpointTest {
public void JoinpointTest(){
System.out.println("**********JoinpointTest*****************");
}
public void test(){
System.out.println("JoinpointTest++++執(zhí)行我正常流水線(xiàn)的業(yè)務(wù)邏輯");
}
}
@Aspect // FOR AOP
@Order(-99) // 控制多個(gè)Aspect的執(zhí)行順序秸滴,越小越先執(zhí)行
@Component
public class AdviceTest {
@Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())")
public void poincut(){
}
@Before("poincut()")
public void beforeTest() {
System.out.println("執(zhí)行 方法 之前 調(diào)用----");
}
@After("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterTest() {
System.out.println();
System.out.println("執(zhí)行 方法 之后 調(diào)用----");
}
@Around("execution(* com.slife.java8.aspect.AspectTest.test())")
public void aroundTest() {
System.out.println();
System.out.println("執(zhí)行 方法 前后 調(diào)用----");
}
@AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterReturningTest() {
System.out.println();
System.out.println("執(zhí)行 方法 AfterReturning 調(diào)用----");
}
@AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())")
public void afterThrowingTest() {
System.out.println();
System.out.println("執(zhí)行 方法 AfterThrowing 調(diào)用----");
}
@Before("execution(* com.slife.java8..*test*(..))")
public void aspecttest1() {
System.out.println();
System.out.println("執(zhí)行 方法aspecttest1 Before 調(diào)用----");
}
}
點(diǎn)擊獲取阿里云優(yōu)惠券
我的官網(wǎng)
[圖片上傳失敗...(image-8a5f4a-1509673150463)]
我的官網(wǎng)http://guan2ye.com
我的CSDN地址http://blog.csdn.net/chenjianandiyi
我的簡(jiǎn)書(shū)地址http://www.reibang.com/u/9b5d1921ce34
我的githubhttps://github.com/javanan
我的碼云地址https://gitee.com/jamen/
阿里云優(yōu)惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=vf2b5zld&utm_source=vf2b5zld