一、AOP
AOP(Aspect Oriented Programing),即(ji)面向切面編程,可以說是OOP(面向對象編程)的(de)(de)補充和(he)完(wan)善。OOP引入封裝、繼承、多臺(tai)等(deng)概(gai)念(nian)來建立一種對象層(ceng)(ceng)次結構,用于(yu)模(mo)擬(ni)公共行為的(de)(de)一個(ge)集合。不過OOP不適(shi)合定義橫向的(de)(de)關(guan)系,比如日志(zhi)功能(neng)。日志(zhi)代(dai)(dai)碼往(wang)往(wang)橫向地(di)散步(bu)在所有對象層(ceng)(ceng)次中,而與(yu)它對應的(de)(de)對象的(de)(de)核(he)心功能(neng)毫無關(guan)系。這種散步(bu)在各處的(de)(de)無關(guan)的(de)(de)代(dai)(dai)碼被稱(cheng)為橫切,在OOP設計中,它導致大量代(dai)(dai)碼的(de)(de)重(zhong)復(fu),不利于(yu)各個(ge)模(mo)塊重(zhong)用。
AOP技術恰恰相(xiang)反(fan),它利用(yong)一種稱為(wei)橫切(qie)的(de)技術,剖解開封裝(zhuang)的(de)對象內部,并(bing)將那些(xie)影響了多個類(lei)的(de)公共行為(wei)封裝(zhuang)到一個可(ke)重用(yong)模塊,并(bing)將其命(ming)名為(wei)"Aspect",即切(qie)面(mian)。
二、AOP核心概念
1、橫切關注點
對哪些(xie)方(fang)法進(jin)行(xing)攔(lan)截,攔(lan)截后怎么(me)處(chu)理,這些(xie)關注(zhu)點稱(cheng)為橫切關注(zhu)點
2、切(qie)面(mian)(aspect)
類是(shi)對(dui)物(wu)體特征(zheng)的(de)抽象,切(qie)面就是(shi)對(dui)橫切(qie)切(qie)點的(de)抽象
3、連接點(joinpoint)
被攔截到的(de)(de)點,因為Spring只(zhi)支持(chi)方(fang)法類(lei)型的(de)(de)連(lian)接點,所以(yi)在(zai)Spring中連(lian)接點指(zhi)的(de)(de)就是(shi)被攔截到的(de)(de)方(fang)法,實際上連(lian)接點還可以(yi)是(shi)字段或者構造器
4、切入點(pointcut)
對連接點進行攔截的定義
5、通知(advice)
所(suo)謂通(tong)知(zhi)指的(de)就是指攔(lan)截(jie)到連接(jie)點之后(hou)要執行(xing)的(de)代碼,通(tong)知(zhi)分為(wei)前置、后(hou)置、異常(chang)、最(zui)終、環(huan)繞通(tong)知(zhi)五類
6、目標對象
代理的目標對象
7、織入(weave)
將切面應用(yong)到目標對象(xiang)并(bing)導致代理對象(xiang)創建的過程(cheng)
8、引(yin)入(introduction)
在(zai)不修改代碼(ma)的(de)前提下,引入可以在(zai)運行期為類動態(tai)的(de)添加一些(xie)方(fang)法或字段
三、Spring對AOP的支持
Spring中AOP代(dai)理(li)由Spring的(de)IOC容器(qi)負(fu)責(ze)生成、管(guan)理(li),其依(yi)賴(lai)關系也(ye)由IOC容器(qi)負(fu)責(ze)管(guan)理(li)。因此,AOP代(dai)理(li)可以直接使用容器(qi)中的(de)其他bean實(shi)例作為目(mu)標,這種關系可由IOC容器(qi)的(de)依(yi)賴(lai)注入(ru)提供。Spring創建代(dai)理(li)的(de)規則為:
1、默認使用JAVA動態代理來創建AOP代理,這樣就可以為任何借(jie)口實例創建代理了
2、當(dang)需要代(dai)理(li)的類不是代(dai)理(li)接口時,Spring會切(qie)換為使用(yong)GCLIB代(dai)理(li),也可以(yi)強(qiang)制使用(yong)CGLIB
AOP編程的步驟:
1、定義普通業務類
2、定(ding)義(yi)切(qie)入(ru)點(dian),一個切(qie)入(ru)點(dian)可(ke)能橫切(qie)多個業務類
3、定義增強處理
四、Spring boot中AOP的使用
1、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
2、創建切(qie)面類(lei),可以通過PointCut定義切(qie)入點,也(ye)可以直接在通知上設置切(qie)入點
@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
/**
* 定義入切點
*/
@Pointcut("execution(* cn.draven.aop.service.*.*(..))")
public void pointCut(){}
@Before("pointCut()")
public void before(JoinPoint jp){
this.logger.info("前置通知.");
}
@After("pointCut()")
public void after(JoinPoint joinPoint){
this.logger.info("后置通知.");
}
@Around("execution(* cn.draven.aop.service.*.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
this.logger.info("環繞前...");
proceedingJoinPoint.proceed();
this.logger.info("環繞后...");
}
}
3、編寫業務類,上(shang)面的切入點定(ding)義包名為cn.draven.aop.service.*的所有方(fang)法都進行攔截
4、進行單元測試
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class AopTestCase {
@Resource
private ILogService iLogService;
@Test
public void test(){
this.iLogService.logTest();
}
}
五、切點表達式
1、分類
- 匹配方法 execution
- 匹配注解 @within、@target、@args、@annotation
- 匹配包/類型 within()
- 匹配對象 target、this
- 匹配參數 args
| 類型 | 描述 |
|---|---|
| execution() | 用于匹配方法執行的連接點 |
| within() | 用于匹配指定的類及其子類中的所有方法 |
| this() | 匹配可以向上轉型為this指定的類型的代理對象中的所有方法 |
| target() | 匹配可以向上轉型為target指定的類型的目標對象中的所有方法 |
| args() | 用于匹配運行時傳入的參數列表的類型為指定的參數列表類型的方法 |
| @within() | 用于匹配持有指定注解的類的所有方法 |
| @target() | 用于匹配的持有指定注解目標對象的所有方法 |
| @args() | 用于匹配運行時傳入的參數列表的類型持有注解列表對應的注解的方法 |
| @annotation() | 用于匹配持有指定注解的方法 |
| bean | bean(Bean的id或名字通配符)匹配特定名稱的Bean對象 |
2、三種通配符
| 通配符 | 說明 |
|---|---|
| * | 匹配任何數量字符 |
| .. | 匹配任何數量字符的重復,如匹配任何數量子包,在方法匹配中匹配任何數量參數 |
| + | 匹配指定類型的子類型;僅能作為后綴放在類型模式后 |
3、操作符
| 操作符 | 說明 |
|---|---|
| &&或者and | 與操作符 |
| `或者or | 或操作符 |
| !或者not | 非操作符 |
4、使用實例
- execution
execution([方法(fa)可見性] 返回類型 [方法所(suo)在類的全路徑名] 方法名(參數類型列表) [方法拋(pao)出的異常類型])
例如、匹配所有(you)目(mu)標(biao)類的public方法
execution(public * * (..))
- within
within(方(fang)法所在類的全路徑名)
例如、匹配com.spring.service包及子包下的所有(you)類
within(com.spring.service..*)
- args
args(參數類(lei)型列表)
例如、第(di)一個參數為java.lang.String,最后一個參數為java.lang.Integer
args(java.lang.String,..,java.lang.Integer)
- this和target
this(方(fang)法所(suo)在類(lei)的全(quan)路徑名)、target(方法(fa)所在類(lei)的(de)全路徑名)
例如、匹配名為(wei)MyObject的代(dai)理對象(xiang)
this(com.draven.MyObject)
例如、匹配名為(wei)MyObject的(de)目標對象
target(com.draven.MyObject)
- @within
@within(注(zhu)解的(de)全(quan)路徑名)
例如(ru)、匹配使用com.spring.annotation.BusinessAspect注解標注的類
@Within(com.spring.annotation.BusinessAspect)
- @annotation
@annotation(注解的全路徑名)
例如、匹配使用(yong)com.spring.annotation.Body注解標(biao)注的方法(fa)
@annotation(com.spring.annotation.Body)
- @args
@args(注解(jie)的全路徑(jing)名)
例(li)如、匹配使用com.spring.annotation.Body注解的類(lei)作為(wei)參數的方法(fa)
@args(com.spring.annotation.Body)
public void test(Apple apple){}
@Body
public class Apple{}