Spring AOP
Spring AOP當中的一些概念
什么是AOP
Aspect Oriented Programming 面向切面編程,在傳統的oop開發過程中,邏輯是自上而下的,譬如我們實現一個登錄功能,瀏覽器發起http請求到controller,controller負責接受請求,封裝參數、驗證參數等等,繼而把數據傳給service,service處理完之后調用dao,dao負責操作db后返回。這個過程當中會產生很多橫切性問題,比如controller當中的日志記錄、比如service當中的權限校驗、比如dao當中的事務處理。這些橫切性的問題和主業務邏輯是沒有關系的,比如日志的記錄失敗與否并不會影響整個登錄邏輯,這些橫切的問題如果不去處理會散落在代碼的各個地方,面向切面編程就是我們程序員編碼只關心橫切性問題的抽象。
什么事 Spring AOP
aop面向切面編程是一種編程目標,說白了是一個標準,而實現這種編程標準的技術手段有很多,比如你可以使用JDK動態代理來實現AOP,也可以使用aspectj這種靜態編程技術來實現代理從而實現aop。Spring AOP就是實現AOP的一種技術手段,相當于AOP=發財(兩種都是一種目的)。Spring AOP=賣腎,發財除了賣腎當然還可以有其他很多路子,比如可以努力工作也可以發財,所以實現AOP除了Spring AOP市面上還有很多技術來實現AOP
Spring AOP的核心原理
對于上述的橫切性問題spirng的做法是把這些問題集中到一個切面(Aspect)當中,然后利用動態代理技術動態增強業務Bean。
Spring AOP的核心技術是JDK動態代理和CGLIB動態代理,加上spring內部的BeanPostProcessor一起工作,從而達到了對spring當中Bean完成增強
Spring AOP當中的專業術語
join point
連接點:程序執行當中的一個點。他是一個最終的結果,因為Spring AOP是最小連接單位的是一個方法,故而一個切點就是一個方法,(但是是被連接了的方法)
point cut
切點:一組連接點的集合。因為在開發 中可能連接點可能會很多,根據業務和功能的不同會進行分組,切點就是一組連接點的描述(相當于連接點=一條數據,切點=一張表,一個數據庫當中可以存在多張表,表可以有多條數據)
Advice
通知:通知可以從三個緯度來理解。
1、通知的內容:也就是你增強一個方法的具體業務邏輯,比如日志記錄,比如事務操作。
2、通知的時機:Before Advice、After Advice、After throwing Advice、After(finally) Advice、Around Advice
3、通知的目標:也就是這個通知需要作用到哪個或者哪些連接點上
Introduction
導入:Spring AOP可以在不修改代碼的情況下讓某個類強制實現某個接口,并且可以指定接口的默認實現方法
Target object
目標對象:由于Sping AOP是借助CGLIB或者JDK動態代理來實現增強的,目標對象的概念和動態代理當中的那個目標對象是一個概念----被代理的那個對象
AOP proxy
代理對象:一個被JDK或者CGLIB動態代理之后的對象
Weaving
織入:一個過程。把切面和應用程序對象連接的過程
Aspect
切面:上述所有概念在編碼過程中存在的類成為切面、切點、連接點、通知等等存在的類稱為一個切面
Spring AOP開發
1、啟動@AspectJ
支持使用java Configuration啟用@AspectJ支持,需要添加@AspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
使用xml配置啟動@AspectJ
<aop:aspectj-autoproxy/>
2、聲明一個Aspect
聲明一個@Aspect注解類,并且定義為一個bean交給Spring管理
@Component
@Aspect
public class UserAspect {
}
3、聲明一個pointCut
切入點表達式由@Pointcut注解表示,切入點聲明有兩部分組成:一個簽名包含名稱和任何參數,以及一個切入點表達式,該表達式確定我們對哪個方法執行感興趣
切入點確定感興趣的join points(連接點),從而使我們能夠控制何時執行通知。Spring AOP只支持Spring Bean的方法執行join points(連接點),所以可以將切入點看做是匹配Spring Bean上方法的執行
@Aspect
@Component
public class UserAspect {
@Pointcut("execution(* transfer(..))")// 切入點表達式
private void pointcut() { //切入點簽名
}
}
4、聲明一個Advice通知
advice通知與pointcut切入點表達式相關聯,并在切入點匹配的方法執行@Before之前,@After之后或前后運行
// 申明Aspect,并且交給spring管理
@Aspect
@Component
public class UserAspect {
/**
* 聲明切入點,匹配User到所有的方法調用
* execution 匹配方法執行連接點
* within:將撇撇限制為特定的連接點
* args:參數
* target:目標對象
* this:代理對象
*/
@Pointcut("execution(* transfer(..))")// 切入點表達式
private void pointcut() { //切入點簽名
}
/**
* 聲明before通知,在pointcut切入點前執行
* 通知與切入點表達式相關聯
* 并且切入點匹配的方法執行之前,之后或者前后運行
* 切入點表達式可以是指定切入點的簡單引用,也可以是在適當的位置聲明切入點表達式
*/
@Before("pointCut()")
public void adviceBefore() {
log.info("before aop");
}
}
各種連接點joinpoint的意義
1、execution
用于匹配方法執行join points連接點,最小粒度方法,在aop中主要使用
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
這里問號標識當前項目可以有也可以沒有,其中各項的語義如下:
modifiers-pattern:方法的可見性,如public,protected
ret-type-pattern:方法的返回值類型,如int,void
declaring-type-pattern:方法所在類的全路徑名,如com.spring.aspect
param-pattern:方法參數類型 比如java.lang.String
throws-pattern:方法跑出的異常類型 如java.lang.Exception
example:
@Pointcut("execution(* com.spring.dao.*.*(..))") // 匹配com.spring.dao下的任意接口和類的任意方法
@Pointcut("execution(public * com.spring.dao.*.*(..))") //匹配com.spring.dao下的任意接口和類的public方法
@Pointcut("execution(public * com.spring.dao.*.*())") //匹配com.spring.dao下的任意接口和類的public 無參方法
@Pointcut("execution(* com.spring.dao.*.*(java.lang.String, ..))") //匹配com.spring.dao下的任意接口和類第一個參數且是String類型的方法
@Pointcut("execution(* com.spring.dao.*.*(java.lang.String))") //匹配com.spring.dao下的任意接口和類只有一個參數且是String類型的方法
@Pointcut("execution(public * *(..))") //匹配任意接口和類public的方法
@Pointcut("execution(* te*(..))") //匹配任意接口和類te開頭的方法
@Pointcut("execution(* com.spring.dao.ADao.*(..))") //匹配ADao類下的所有方法
@Pointcut("execution(* com.spring.dao..*.*(..))") //匹配com.spring.dao包及其子包中任意的方法
關于這個表達式的詳細寫法
https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html
由于Spring切面粒度最小是達到方法級別,而execution表達式可以用于明確指定方法返回類型、類名、方法名和參數名等 與方法相關的信息,并且在Spring中,大部分需要使用AOP的業務場景中也只需要達到方法級即可,因而execution表達式的使用最廣泛的
2、within
該表倒是的最小粒度為類
within與execution相比,粒度更大,僅能實現到包和接口、類級別,而execution可以精確到方法的返回值、參數個數、修飾符、參數類型等
@Pointcut("within(com.spring.dao.*") //匹配com.spring.dao包中任意的方法
@Pointcut("within(com.spring.dao..*") //匹配com.spring.dao包及其子包中任意的方法
3、args
args表達式的作用是匹配指定參數類型和指定參數數量的方法,與包名和類名無關
@Pointcut("args(java.lang.Integer, java.lang.String)")
args通execution不同的地方在于:args匹配的是運行時傳遞給方法的參數類型 execution(* *(java.io.Serializable))匹配的是方法在聲明時指定的方法參數類型
4、this
JDK代理時,指向接口和代理類proxy,cglib代理時,指向接口和子類(不適用proxy)
5、target
target 指向接口和子類
@Pointcut("target(com.spring.dao.ADaoImpl)") // 目標對象,也就是被代理的對象,限制目標對象為com.spring.dao.ADaoImpl類
@Pointcut("this(com.spring.dao.ADaoImpl)") // 當前對象,也就是代理的對象,代理對象是通過代理目標對象的方式獲取新的對象,與原值并非是一個
@Pointcut("@target(com.spring.anno.A)") // 具有@A的目標對象中的任意方法
@Pointcut("@within(com.spring.anno.A)") // 等同于target

浙公網安備 33010602011771號