Spring AOP 詳解

情景案例

小明辛苦忙了一全年終於完成了包含300個接口的業務系統項目。項目圓滿上線並穩定運行了一段時間了。忽然有一天總監說,對於會形成數據變化的全部接口,咱們必須記錄用戶的操做日誌。而後小明就吭哧吭哧給其中150個接口,挨個加上日誌代碼,累得真夠嗆。spring

過了一陣子總監又說,全部變化不多的數據所有都加上緩存,緩存涉及到刷新緩存、獲取緩存、刪除緩存的問題。因而乎,小明就又吭哧吭哧地給其中的100個接口加上緩存相關的代碼。數據庫

又過了一陣子總監說,全部涉及充值退款費用相關的接口,須要生成發票單存入數據庫。這時候小明又須要吭哧吭哧給涉及到的50個接口,挨個加上發票存儲操做。編程

小明每天加班也沒在工期內完成任務,而且本來的業務代碼已經變得臃腫不堪了。設計模式

本來的代碼:緩存

/**
 * 業務方法
 */
public static void method() {

    // 業務操做
    doBusiness();

}
複製代碼

通過硬編碼添加各類非業務性代碼後的業務代碼:bash

/**
 * 業務方法
 */
public static void method() {
    // 日誌操做
    doLog();
    // 業務操做
    doBusiness();
    // 緩存操做
    doLog();
    // 發票操做
    doReceipt();

}
複製代碼

讀者應該能明顯感覺到在沒有AOP代理的狀況下的缺點框架

  1. 業務代碼和非業務代碼混雜在一塊兒,本來清晰的業務流程淹沒在與業務不相關的代碼中。
  2. 增長非業務性的功能時,都須要手工硬編碼去實現,費時費力。
  3. 代碼變得很差維護,一是代碼耦合度高,二是須要經過硬編碼的方式去拓展或者修改功能。

AOP是什麼?

在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。主要目標仍是致力於解耦,咱們能夠看到解耦這一理念貫穿於咱們的整個編碼工做中。咱們經過各類設計模式或者設計原則來使得對象之間解耦。經過Spring IOC容器中利用依賴注入使得對象之間的耦合度更低。而AOP的思想解耦得更完全,經過動態的添加功能來加強實現,而且作到毫無代碼的侵入性。利用AOP能夠對業務邏輯和非業務邏輯的部分進行隔離,能夠提取非業務邏輯的部分,提升程序的可重用性,同時提升了開發的效率。ide

如何理解「切面」二字呢?函數

15703790331.png

咱們的業務流程方法都是自頂向下垂直的,而當咱們須要給這些業務方法統一加上某些非業務功能的話,就會發現這些非業務功能方法在圖上會連成一條直線,並與原來的業務流程方法垂直橫切。性能

爲何使用AOP?

  1. 核心業務代碼與切面代碼解耦,切面代碼對核心業務代碼徹底無侵入,遵照單一職責原則,徹底隔離核心業務代碼與切面代碼。

  2. 低耦合帶來可維護性高,修改或者新增一個切面代碼僅需集中在一處進行更改。低耦合也意味着切面代碼可複用性高。

  3. Spring IOC容器自然地爲AOP的實現提供了便利,IOC和AOP的結合使得Spring的解耦能力更強。

AOP例子

先聲明切面類:

/**
 *  註解@Aspect標識該類爲切面類
 */
@Component
@Aspect
public class PersonAspect {

    // 經過表達式定義切入點
    @Pointcut("execution(* com.valarchie.aop.MeetingServiceImpl.meeting(..))")
    public void conference() {}
 
    // 前置通知
    @Before("meeting()")
    public void takeSeats() {
        System.out.println("開會前,找到位置坐");
    }
 
    // 前置通知
    @Before("meeting()")
    public void silenceCellPhones() {
        System.out.println("開會前,手機調成靜音");
    }
 
    // 後置通知
    @After("meeting()")
    public void summary() {
        System.out.println("開會後,寫總結報告");
    }
 
}

複製代碼

建立要被代理的接口,即MeetingService會議服務

public interface MeetingService {
    void meeting();
}
複製代碼

建立MeetingService會議服務的具體實現

@Component
public class MeetingServiceImpl implements MeetingService {
 
    @Override
    public void meeting() {
        System.out.println("會議進行中..");
    }
 
}

複製代碼

定義AOP配置

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.valarchie")
public class AppConfig {
 
}
複製代碼
  1. 註解@EnableAspectJAutoProxy開啓代理;
  2. 若是屬性proxyTargetClass默認爲false, 表示使用jdk動態代理織入加強;
  3. 若是屬性proxyTargetClass設置爲true,表示使用Cglib動態代理技術織入加強;
  4. 若是屬性proxyTargetClass設置爲false,可是目標類沒有聲明接口, Spring aop仍是會使用Cglib動態代理,也就是說非接口的類要生成代理都用Cglib。

測試AOP:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class AopTest {
 
    @Autowired
    private MeetingServiceImpl meetingService;
 
    @Test
    public void testAopAnnotation() {
        meetingService.meeting();
    }
 
}

複製代碼

運行結果:

開會前,手機調成靜音
開會前,找到位置坐
會議進行中..
開會後,寫總結報告
複製代碼

在這個AOP的例子當中咱們沒有在會議服務實現類當中硬編碼須要添加的切面功能,而是經過另外新建一個類來描述切面,以及須要在切面上加強的功能。這樣的實現是否是更優雅呢?

關於切入點的表達式稍微解析一下:

例如定義切入點表達式  execution (* com.sample.service.impl..*.*(..))

execution()是最經常使用的切點函數,其語法以下所示:

 整個表達式能夠分爲五個部分:

 一、execution(): 表達式主體。

 二、第一個*號:表示返回類型,*號表示全部的類型。

 三、包名:表示須要攔截的包名,後面的兩個句點表示當前包和當前包的全部子包,
 com.sample.service.impl包、子孫包下全部類的方法。

 四、第二個*號:表示類名,*號表示全部的類。

 五、*(..):最後這個星號表示方法名,*號表示全部的方法,後面括弧裏面表示方法
 的參數,兩個句點表示任何參數。
複製代碼

以上的例子當中涉及很多AOP概念,接下來咱們針對這些概念進行逐一解釋。

AOP中的概念闡述

  • 鏈接點(Joinpoint) 程序執行的某個特定位置:如類開始初始化前、類初始化後、類某個方法調用前、調用後、方法拋出異常後。一個類或一段程序代碼擁有一些具備邊界性質的特定點,這些點中的特定點就稱爲「鏈接點」。Spring僅支持方法的鏈接點,即僅能在方法調用前、方法調用後、方法拋出異常時以及方法調用先後這些程序執行點織入加強。鏈接點由兩個信息肯定:第一是用方法表示的程序執行點;第二是用相對點表示的方位。

  • 切點(Pointcut) 每一個程序類都擁有多個鏈接點,如一個擁有兩個方法的類,這兩個方法都是鏈接點,即鏈接點是程序類中客觀存在的事物。AOP經過「切點」定位特定的鏈接點。鏈接點至關於數據庫中的記錄,而切點至關於查詢條件。切點和鏈接點不是一對一的關係,一個切點能夠匹配多個鏈接點。在Spring中,切點經過org.springframework.aop.Pointcut接口進行描述,它使用類和方法做爲鏈接點的查詢條件,Spring AOP的規則解析引擎負責切點所設定的查詢條件,找到對應的鏈接點。其實確切地說,不能稱之爲查詢鏈接點,由於鏈接點是方法執行前、執行後等包括方位信息的具體程序執行點,而切點只定位到某個方法上,因此若是但願定位到具體鏈接點上,還須要提供方位信息。

  • 加強(Advice) 加強是織入到目標類鏈接點上的一段程序代碼,在Spring中,加強除用於描述一段程序代碼外,還擁有另外一個和鏈接點相關的信息,這即是執行點的方位。結合執行點方位信息和切點信息,咱們就能夠找到特定的鏈接點。

  • 目標對象(Target) 加強邏輯的織入目標類。若是沒有AOP,目標業務類須要本身實現全部邏輯,而在AOP的幫助下,目標業務類只實現那些非橫切邏輯的程序邏輯,而性能監視和事務管理等這些橫切邏輯則可使用AOP動態織入到特定的鏈接點上。

  • 引介(Introduction) 引介是一種特殊的加強,它爲類添加一些屬性和方法。這樣,即便一個業務類本來沒有實現某個接口,經過AOP的引介功能,咱們能夠動態地爲該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。

  • 織入(Weaving) 織入是將加強添加對目標類具體鏈接點上的過程。AOP像一臺織布機,將目標類、加強或引介經過AOP這臺織布機完美無缺地編織到一塊兒。根據不一樣的實現技術,AOP有三種織入的方式: a、編譯期織入,這要求使用特殊的Java編譯器。 b、類裝載期織入,這要求使用特殊的類裝載器。 c、動態代理織入,在運行期爲目標類添加加強生成子類的方式。 Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。

  • 代理(Proxy) 一個類被AOP織入加強後,就產出了一個結果類,它是融合了原類和加強邏輯的代理類。根據不一樣的代理方式,代理類既多是和原類具備相同接口的類,也可能就是原類的子類,因此咱們能夠採用調用原類相同的方式調用代理類。

  • 切面(Aspect) 切面由切點和加強(引介)組成,它既包括了橫切邏輯的定義,也包括了鏈接點的定義,Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的鏈接點中。

筆者我的理解,若有錯誤懇請網友評論指正。

轉自個人我的博客 vc2x.com

相關文章
相關標籤/搜索