本文小福利 點(diǎn)我獲取阿里云優(yōu)惠券
AOP核心概念
1、橫切關(guān)注點(diǎn)
對(duì)哪些方法進(jìn)行攔截,攔截后怎么處理,這些關(guān)注點(diǎn)稱之為橫切關(guān)注點(diǎn)
2、切面(aspect)-》(通知+切點(diǎn))
類是對(duì)物體特征的抽象,切面就是對(duì)橫切關(guān)注點(diǎn)的抽象。
通知+切點(diǎn)
意思就是所有要被應(yīng)用到增強(qiáng)(advice)代碼的地方。(包括方法的方位信息)
3、連接點(diǎn)(joinpoint)-》(被攔截的方法)
被攔截到的點(diǎn),因?yàn)镾pring只支持方法類型的連接點(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)繞通知五類
這玩意也叫 增強(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)行期為類動(dòng)態(tài)地添加一些方法或字段
圖解
上面那一堆看不懂對(duì)嗎? 我也不太懂。
來(lái)看張圖
通知(Advice)類型
切面一共有五種通知
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 通知包裹在被通知的方法的周圍知。
環(huán)繞通知(Around advice) :包圍一個(gè)連接點(diǎn)的通知,類似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í)行我正常流水線的業(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ù)為指定類型的方法的執(zhí)行動(dòng)作。
@args()
定制join-point去匹配那些參數(shù)被指定類型注解的方法的執(zhí)行動(dòng)作
execution()
開(kāi)始匹配在其內(nèi)部編寫(xiě)的定制
this()
定制join-pont去匹配由AOP代理的Bean引用的指定類型的類。
target()
定制join-point去匹配特定的對(duì)象,這些對(duì)象一定是指定類型的類。
@target()
定制join-point去匹配特定的對(duì)象,這些對(duì)象要具有的指定類型的注解。
within()
定制join-point在必須哪一個(gè)包中。
@within()
定制join-point在必須由指定注解標(biāo)注的類中。
@annotation
定制連接點(diǎn)具有指定的注解。
只有execution用來(lái)執(zhí)行匹配,其他標(biāo)志符都只是為了限制/定制他們所要匹配的連接點(diǎn)的位置。
命名及匿名切入點(diǎn)
類型匹配語(yǔ)法
*:匹配任何數(shù)量字符。
..:匹配任何數(shù)量字符的重復(fù),如在類型模式中匹配任何數(shù)量子包;而在方法參數(shù)模式中匹配任何數(shù)量參數(shù)。
+:匹配指定類型的子類型;僅能作為后綴放在類型模式后邊。
例子
java.lang.String 匹配String類型;
java.*.String 匹配java包下的任何“一級(jí)子包”下的String類型;
如匹配java.lang.String,但不匹配java.lang.ss.String
java..* 匹配java包及任何子包下的任何類型;
如匹配java.lang.String、java.lang.annotation.Annotation
java.lang.*ing 匹配任何java.lang包下的以ing結(jié)尾的類型;
java.lang.Number+ 匹配java.lang包下的任何Number的自類型;
如匹配java.lang.Integer,也匹配java.math.BigInteger
詳細(xì)語(yǔ)法
注解? 修飾符? 返回值類型 類型聲明?方法名(參數(shù)列表) 異常列表?
注解:可選,方法上持有的注解,如@Deprecated;
修飾符:可選,如public、protected;
返回值類型:必填,可以是任何類型模式;“*”表示所有類型;
類型聲明:可選,可以是任何類型模式;
方法名:必填,可以使用“*”進(jìn)行模式匹配;
參數(shù)列表:“()”表示方法沒(méi)有任何參數(shù);“(..)”表示匹配接受任意個(gè)參數(shù)的方法,“(..,java.lang.String)”表示匹配接受java.lang.String類型的參數(shù)結(jié)束,且其前邊可以接受有任意個(gè)參數(shù)的方法;“(java.lang.String,..)” 表示匹配接受java.lang.String類型的參數(shù)開(kāi)始,且其后邊可以接受任意個(gè)參數(shù)的方法;“(*,java.lang.String)” 表示匹配接受java.lang.String類型的參數(shù)結(jié)束,且其前邊接受有一個(gè)任意類型參數(shù)的方法;
異常列表:可選,以“throws 異常全限定名列表”聲明,異常全限定名列表如有多個(gè)以“,”分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。
匹配Bean名稱:可以使用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獲?。篠pring AOP提供使用org.aspectj.lang.JoinPoint類型獲取連接點(diǎn)數(shù)據(jù),任何通知方法的第一個(gè)參數(shù)都可以是JoinPoint(環(huán)繞通知是ProceedingJoinPoint,JoinPoint子類),當(dāng)然第一個(gè)參數(shù)位置也可以是JoinPoint.StaticPart類型,這個(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é)生成、管理,其依賴關(guān)系也由IOC容器負(fù)責(zé)管理。因此,AOP代理可以直接使用容器中的其它bean實(shí)例作為目標(biāo),這種關(guān)系可由IOC容器的依賴注入提供。Spring創(chuàng)建代理的規(guī)則為:
1、默認(rèn)使用Java動(dòng)態(tài)代理來(lái)創(chuàng)建AOP代理,這樣就可以為任何接口實(shí)例創(chuàng)建代理了
2、當(dāng)需要代理的類不是代理接口的時(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í)行我正常流水線的業(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.itdecent.cn/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