Spring AOP你會用嗎?一看就明白

關注公衆號【愛做夢的錘子】回覆【java知識點】可下載我整理的完整版思維導圖

SpringBoot 2.1.5.RELEASE
java version 1.8.0_231

一、什麼是Spring AOP?

對這個名詞的概念解釋相信大家都不陌生,Spring AOP即是Spring的核心特性之一,AOP 全拼 Aspect Oriented Program,面向切面編程。
從字面意思理解切面編程的意思就好像是我們用“刀子”把一個順序執行的代碼從某一部位切開,然後我們在這個切面上寫上我們的代碼。AOP技術的目的是爲了方便開發者,在切面編程的思想中,開發人員需要將核心業務和周邊業務區分開。
核心業務就是涉及系統中核心部分的業務邏輯,比如操作數據庫,修改重要用戶數據等,這些業務不具有普適性,每個業務方法都有各自不同的處理邏輯;
周邊業務就是一些共性邏輯操作,比如一些通用日誌打印,方法耗時統計等等

幾個名詞概念

  • 連接點(Joinpoint):在程序執行過程中某個特定的點,比如某方法調用的時候或者處理異常的時候,即在什麼時間,在Spring中連接點概念就是代表着通知的五種類型:before、around、after、afterReturning、afterThrowing

什麼時間:方法執行前

  • 通知(Advice):在切面的某個特定的連接點上執行的動作。即你在某個時間(連接點)想做具體什麼事情(功能),具體事情就是你實際的業務,比如打印日誌,提交事務等。

具體什麼事情:打印日誌

  • 切入點(Pointcut):匹配連接點的斷言。可以理解爲一個選擇器,即我的功能是要在哪些類的哪些方法執行,可以使用通配符來選擇類和方法

哪些類的哪些方法:* site.teamo.mall.service.impl….(…))
理解爲:在site.teamo.mall.service.impl包下的所有類的所有方法

  • 切面(Aspect):一個關注點的模塊化,這個關注點可能會橫切多個對象。就是通知和切入點結合,即在哪些類的哪些方法上,在什麼時間,做什麼事情。

將通知和切入點結合:在site.teamo.mall.service.impl包下的所有類的所有方法在方法執行前打印日誌

  • 引入(Introduction):用來給一個類型聲明額外的方法或屬性,即將切面引入切入點匹配的對應類中。

  • 目標對象(Target Object): 被一個或者多個切面所通知的對象,即切入點匹配的對應類的實例就是目標對象。

  • 代理(AOP Proxy):AOP框架創建的對象,用來實現切面契約。AOP實現的一種方式,對目標對象進行代理,以實現切面通知

  • 織入(Weaving):把切面連接到其它的應用程序類型或者對象上,並創建一個被通知的對象。即對目標對象進行代理,將切面加入目標對象的過程

整體流程理解

在Spring AOP核心的四點是:通知、切入點、切面、織入。一個完整的流程可以描述爲:編寫你的增強功能(通知),確定這個功能的執行時間(連接點),確定這個功能在什麼地方生效(切入點),經過以上三步就形成了一個切面,使用Spring的動態代理機制對目標對象進行代理,將切面引入目標對象的執行邏輯,這樣切面就被織入了目標對象

Spring AOP中五種通知類型

  • before:在方法調用之前執行的通知
  • around:在方法調用之前和之後,都分別可以執行的通知
  • after:在方法正常調用之後執行的通知
  • afterThrowing:在方法調用過程中發生異常時執行的通知
  • afterReturning:在方法調用之後執行的通知,不論方法是否執行異常

二、一個場景

找出慢方法:現有系統A,系統A中的代碼中存在調用外部服務接口的邏輯,對此需要統計系統A服務層中各個方法的執行時長,找出執行慢的方法對其統計,以便後續優化。

一般邏輯

methodA(){
	long start = currentTime();
	doSomething;//方法A核心業務邏輯
	...
	if(currentTime-start>500){
		LoG.info(方法執行時長過長,記爲慢方法)}
}

methodB(){
	long start = currentTime();
	doSomething;//方法B的核心業務邏輯
	...
	if(currentTime-start>500){
		LoG.info(方法執行時長過長,記爲慢方法)}
}
...
...

這樣的一個邏輯中就是將核心業務和周邊業務沒有進行區分,在這樣的一個場景中核心業務就是各自方法自己具體的業務邏輯(methodA和methodB自己的功能邏輯),而周邊業務就是計算一個各自核心業務的執行時間執行較慢時則打印日誌。在這種實現方式中,雖然滿足了當前需求即找出慢方法,可以當週邊業務需求發生變更時(比如要將慢方法的執行時間記錄進數據庫),會帶來較多的代碼改動,存在風險。

切面編程邏輯

avatar

記錄開始時間和計算耗時是周邊業務,methodA的功能邏輯是核心業務,將周邊業務寫在切面中,就將核心業務和周邊業務分開來編程了,耦合性也就降低

三、具體實現

  • pom中引入spring aop的依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

  • 編寫切面代碼

切面表達式eg:execution(* site.teamo.mall.service.impl….(…))
avatar

  • 1——execution 所要執行的表達式主體
  • 2——代表方法返回值類型, *代表匹配所有類型
  • 3——切入點的類在site.teamo.mall.service.impl包及其子包
  • 4——代表匹配切入點包下的類,*代表匹配所有類
  • 5——切入點類的方法,*代表所有方法,(…)代表方法入參,結合起來即所有入參的所有方法

@Aspect
@Component
public class SlowMethodStatistics {

    private static final Logger LOGGER = LoggerFactory.getLogger(SlowMethodStatistics.class);

    @Around("execution(* site.teamo.mall.service.impl..*.*(..))")
    public Object slowMethodLog(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        // 執行目標 service
        Object result = joinPoint.proceed();
        // 記錄結束時間
        long cost = System.currentTimeMillis() - start;
        if (cost > 1) {
            LOGGER.info("======{}.{} 執行耗時:{}======",
                    joinPoint.getTarget().getClass(),
                    joinPoint.getSignature().getName(),
                    cost);
        }
        return result;
    }
}

  • 測試代碼

@SpringBootTest
@RunWith(SpringRunner.class)
@EnableAutoConfiguration
@MapperScan(basePackages = {"site.teamo.mall.dao"})
public class AopTest {

    @Autowired
    private UserService userService;

    @Test
    public void aopTest() throws Exception {
        userService.queryUsernameIsExist("mall");
        userService.queryUserForLogin("test","12345");
    }

}

執行效果
avatar

文章歡迎轉載,轉載請註明出處,個人公衆號【愛做夢的錘子】,全網同id,個站 http://te-amo.site,歡迎關注,裏面會分享更多有用知識,還有我的私密照片

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章