1.AOP概念
在開發過程中會產生一些與主業務邏輯關係不大,但散落在代碼各個部分難以維護的橫切性問題,如日誌記錄、權限驗證、事務管理、異常處理等。AOP就是處理這些橫切性問題,AOP思想就是將這些問題與主業務分開,從而達到與主業務解耦的目的。
2.AOP術語
(1) Join point: 連接點,目標對象中的方法
(2) Advice: 通知,特定連接點上具體操作,具體有"around"、"before"和"after"三種通知
(3) Pointcut: 切點,多個連接點的集合,可用表達式表示目標連接點集合
(4) Target object: 目標對象,即需要增加的對象
(5) Aop proxy: 代理對象,Aop底層爲jdk動態代理和cgLib代理,aop實質即爲目標對象生成代理對象,從而對目標對象進行增強
(6) Weaving: 織入,將需要增強的通知增強進目標對象中
3. @AspectJ 支持
Spring對Aop的支持,底層自己實現了一套AOP邏輯,只是引入了AspectJ的註解支持,AOP是一種概念,SpringAop、AspectJ均爲Aop的實現,Spring實現的Aop邏輯複雜,故引入了AspectJ的註解,底層仍爲自己實現
3.1 Spring中引入切面實現
(1) 引入AspectJ依賴,目的爲提供AspectJ註解支持
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
(2) 配置文件中增加@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.study.spring")
@EnableAspectJAutoProxy
public class SpringConfig {
}
(3) 書寫切面對象,並將該對象交由Spring管理
@Aspect
@Component
public class UserAop {
}
(4)書寫切入點
@Pointcut("execution(* com.study.spring.dao.*(..))")
public void pointCut(){
}
(5)書寫通知即可完成切面實現
@Before("pointCut()")
public void beforeAdvice(){
System.out.println("before advice");
}
3.2 pointcut切面表達式
- execution
表示匹配連接點對應的包及其子包對象及對象中的方法,最小作用單元爲方法級別
execution(* * com.study.spring.dao.*.*(..))
表示式解析:
第一個*:表示方法訪問級別,可以爲public
,private
,protect
,下述表達式表示只會給訪問級別爲public
的方法進行增aop
execution(public * com.study.spring.dao.*.*(..))
第二個*:表示方法返回類型,下述表達式表示只會給沒有返回值的方法進行aop
execution(void com.study.spring.dao.*.*(..))
第三個表示式:表示方法路徑,下述表示dao包及其所有子包,對應所有方法均爲被增強
execution(* com.study.spring.dao..*(..))
execution表達式爲我們常用表達式,表達式也比較多,在此就不做過多介紹,實際開發中可根據實際需求進行配置
- within
表示匹配連接點對應的包及其子包對象,最小作用單元爲對象級別
(1)表示dao包下所有對象均會被增強
within(void com.study.spring.dao.*)
(2)下述表達式表示dao包及其子包中所有對象均會被增強
within(void com.study.spring.dao..*)
- target 及this
target:表示目標對象,即aop對應的原始對象
this:表示代理對象
/**
* 此處需要注意的是,如果配置設置proxyTargetClass=false,或默認爲false,則是用JDK代理,否則使用的是CGLIB代理
* JDK代理的實現方式是基於接口實現,代理類繼承Proxy,實現接口。
* 而CGLIB繼承被代理的類來實現。
* 所以使用target會保證目標不變,關聯對象不會受到這個設置的影響。
* 但是使用this對象時,會根據該選項的設置,判斷是否能找到對象。
*/
@Pointcut("target(com.study.dao.IndexDaoImpl)")//目標對象,也就是被代理的對象。限制目標對象爲com.study.dao.IndexDaoImpl類
@Pointcut("this(com.study.dao.IndexDaoImpl)")//當前對象,也就是代理對象,代理對象時通過代理目標對象的方式獲取新的對象,與原值並非一個
@Pointcut("@target(com.study.anno.ExtAnno)")//具有@ExtAnno的目標對象中的任意方法
@Pointcut("@within(com.study.anno.ExtAnno)")//等同於@target
- @target、@arges、@within、@annotation
帶有@,與上述表示方式類似,唯一區別在於這些表達式只針對於註解形式
切點配置的表達式較大,可根據實際開發中所遇具體配置,在此不做過多介紹
3.3 Advice通知
(1)前置通知(@before):表示在方法執行前進行增強
@Before("pointCut()")
public void beforeAdvice(){
System.out.println("before advice");
}
(2)後置通知(@After):表示在方法執行後進行增強
@After("pointCut()")
public void beforeAdvice(){
System.out.println("after advice");
}
(3)環繞通知(@Around):表示在方法執行前、執行後均增強
@Around("pointCut()")
public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("before around advice");
pjp.proceed();
System.out.println("after around advice");
}
pjp.proceed()
方法表示目標對象中要增加的方法,即爲連接點方法
pjp
常用方法:
getArgs()
:返回方法參數
getThis()
:返回代理對象
getTarget()
:返回目標對像`