什麼是OOP?
面向對象編程,也稱爲OOP(即Object Oriented Programming),通過的是繼承、封裝和多態等概念來建立一種對象層次結構,用於模擬公共行爲的一個集合,最大的優點在於能夠將業務模塊進行封裝,從而達到功能複用的目的。
什麼是AOP?
面向切面編程,也稱爲AOP(即Aspect Oriented Programming),指的是將一定的切面邏輯按照一定的方式編織到指定的業務模塊中,從而將這些業務模塊的調用包裹起來。OOP從縱向上區分出一個個的類來,而AOP則從橫向上向對象中加入特定的代碼。AOP採用"橫切"的技術,解剖開封裝的對象內部,將影響了多個類的公共行爲封裝到一個可重用模塊。將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未來的可操作性和可維護性。
AOP相關術語
1.切面(Aspect)
指定了當前切面邏輯所要包裹的業務模塊的範圍大小,定義了切面的全部內容——它是什麼,在何時和何處完成其功能。
2.通知(Advice)
通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還解決了何時執行這個工作的問題。
- @Before:該註解標註的方法在業務模塊代碼執行之前執行,其不能阻止業務模塊的執行,除非拋出異常;
- @AfterReturning:該註解標註的方法在業務模塊代碼執行之後執行;
- @AfterThrowing:該註解標註的方法在業務模塊拋出指定異常後執行;
- @After:該註解標註的方法在所有的Advice執行完成後執行,無論業務模塊是否拋出異常;
- @Around:該註解功能最爲強大,其所標註的方法用於編寫包裹業務模塊執行的代碼,其可以傳入一個ProceedingJoinPoint用於調用業務模塊的代碼,無論是調用前邏輯還是調用後邏輯,都可以在該方法中編寫,甚至其可以根據一定的條件而阻斷業務模塊的調用;
- @DeclareParents:其是一種Introduction類型的模型,在屬性聲明上使用,主要用於爲指定的業務模塊添加新的接口和相應的實現。
3.連接點(Joint point)
是指在程序執行期間的一個點,比如某個方法的執行或者是某個異常的處理。在Spring AOP中,一個連接點往往代表的是一個方法執行
。
4.切入點(Pointcut)
是指匹配連接點的一個斷言。通知是和一個切入點表達式關聯的,並且在任何被切入點匹配的連接點上運行(舉例,使用特定的名字執行某個方法)。AOP的核心就是切入點表達式匹配連接點的思想。
切點表達式
- execution:Spring切面粒度最小是達到方法級別,而execution表達式可以用於明確指定方法返回類型,類名,方法名和參數名等與方法相關的,execution表達式的語法:
-
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.example.demo.controller;
- name-pattern:方法名類型,如EatWMZ();
- param-pattern:方法的參數類型,如java.lang.String;
- throws-pattern:方法拋出的異常類型,如java.lang.Exception;
如:
execution(public * com.example.demo.controller.AopController.*(..)))
2.within:within表達式的粒度爲類,其參數爲全路徑的類名(可使用通配符),表示匹配當前表達式的所有類都將被當前方法環繞。
如:
within( com.example.demo.controller.AopController)
表示匹配com.example.demo.controller.AopController 下的所有方法
5.
引入(Introduction)
代表了對一個類型額外的方法或者屬性的聲明。Spring AOP允許引入新接口到任何被通知對象(以及一個對應實現)。比如,可以使用一個引入去使一個bean實現IsModified
接口,從而簡化緩存機制。(在AspectJ社區中,一個引入也稱爲一個inter-type declaration類型間聲明)
6.目標對象(Target object)
是指被一個或多個切面通知的那個對象。也指被通知對象("advised object"
),由於Spring AOP是通過運行時代理事項的,這個目標對象往往是一個代理對象
。
7.AOP 代理(AOP proxy)
是指通過AOP框架創建的對象,用來實現切面合約的(執行通知方法等等)。在Spring框架中,一個AOP代理是一個JDK動態代理
或者是一個CGLIB代理
。
8.織入(Weaving)
將切面和其他應用類型或者對象連接起來,創建一個被通知對象。這些可以在編譯時(如使用AspectJ編譯器)、加載時或者運行時完成。Spring AOP,比如其他純Java AOP框架一般是在運行時
完成織入。
SpringBoot中集成AOP
1.pom.xml引入
maven依賴添加如下
<!--引入SpringBoot的Web模塊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入AOP依賴-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
2.定義切面類
@Aspect 註解 使之成爲切面類
@Component 註解 把切面類加入到IOC容器中
package com.example.demo.aop;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 餓鳥嘛配送
**/
@Aspect
@Component
public class EatAop {
/**
*@description 定義切入點,切入點爲com.example.demo.controller.AopController中的所有函數
*@description 通過@Pointcut註解聲明頻繁使用的切點表達式
* */
@Pointcut("execution(public * com.example.demo.controller.AopController.*(..)))")
public void EatAop()
{
}
/**
* @description 在連接點執行之前執行的通知
*/
@Before("EatAop()")
public void doBeforeEat(){
System.out.println("餓鳥嘛配送員登錄接單系統爲接單做準備!");
}
/**
* @description 在連接點執行之後執行的通知(返回通知和異常通知的異常)
*/
@After("EatAop()")
public void doAfterEat(){
System.out.println("餓鳥嘛配送員爲今天的外賣單量感到異常喫驚!");
}
/**
* @description 在連接點執行之後執行的通知(返回通知)
*/
@AfterReturning("EatAop()")
public void doAfterReturningEat(){
System.out.println("返回的通知:餓鳥嘛配送員接單完畢,並會心一笑百媚生!");
}
/**
* @description 在連接點執行之後執行的通知(異常通知)
*/
@AfterThrowing("EatAop()")
public void doAfterThrowingGame(){
System.out.println("異常通知:服務錯誤,請聯繫餓鳥嘛系統管理員!");
}
}
3.新建AopController
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class AopController {
@RequestMapping(value = "/EatWMZ")
public void EatWMZ(){
System.out.println("王麻子點了一份瓜皮香鍋");
}
@RequestMapping(value = "/EatZS")
public void EatZS(){
System.out.println("張三點了一份爆漿牛丸");
}
}
訪問http://127.0.0.1:8080/api/EatWMZ:
小結
本文首先對AOP進行了簡單介紹,然後介紹了切面中的相關術語,舉例說明切點表達式其中常用的兩種,最後做了簡單的demo旨在說明切面邏輯(advice)下各個類型執行的順序。