spring專題系列之AOP的理解和分析

今天是五一收假以來的第一天,距離下一週一共還有四天,這周主要是對Spring的重學習。這四天內我將從以下幾個角度進行分析和理解。

(1)AOP的理解和分析

(2)IOC的理解和分析

(3)對bean的理解和分析

(4)spring配置文件的理解和分析

首先今天對AOP的理解和分析,對aop的理解也分開闡述,對一個技術的思考,基本上都是從上學時候課本闡述一個新知識的順序進行的。 第一:什麼是AOP?

第二:爲什麼要引入AOP?或者是AOP解決了什麼問題?

第三:如何簡單的實現AOP?

第四:AOP對我們寫代碼時候的思考和幫助是什麼?

現在,開始對AOP進行闡述,

1 什麼是AOP?

1.1 概念介紹

Spring的使命是簡化Java代碼開發,aop作爲Spring的一個子模塊,也不例外。

AOP 是 Aspect Oriented Programming(面向切面編程) 的簡稱,和OOP(面向對象編程)一樣是一種編程思想,是對OOP的一種補充。

如何理解什麼是AOP呢?以日誌爲例,在很多管理系統,比如訂單系統、推送系統等等都需要把日誌記錄下來。如果每個業務邏輯裏面都寫日誌的相關代碼,那就重複太多了。

乾脆把日誌的相關邏輯代碼,統一封裝起來。然後在需要的地方嵌入即可。AOP也主要就是做嵌入這件事的。看下面這張圖。

新增訂單編輯訂單取消訂單推送訂單日誌事務

AOP旨在將橫切關注點(crosscutting concern)從業務主體邏輯中進行剝離,實現關注點分離,以提高程序的模塊化程度(及業務模塊只需關注業務邏輯,無需關注日誌、安全、事務等通用邏輯)

上面提到了一些名詞,比如說,切面、關注點、橫切等,下面對這些名詞進行解釋。

1.2 名詞解釋

  1. AOP有自己的一套術語,我們必須瞭解一下這些行話,才能更好地理解AOP。爲了方便大家理解,下面將用課代表收作業作爲例子。

    1. 通知 (Advice)

      定義了在收作業前後需要做的事。常見的通知類型有:before、after、after-returning、around等。

    2. 連接點 (JoinPoint)

      連接點指程序運行時允許插入切面的一個點,可以是一個函數、一個包路徑、一個類、或者拋出的異常。有點類似於可以收作業的時間點。

    3. 切點(PointCut)

      切點用於定義切面的位置,也就是捕獲哪些連接點的調用然後執行"通知"的操作(什麼地點)。

    4. 切面(Aspect)

      切面是切點和通知的聚合,定義了在哪一個切點做什麼通知。

    5. 目標對象( Target )

      指被切面織入的對象。

    6. 引入(Introduction)

      引入允許我們向現有的類添加新方法或屬性。

    7. 織入(Weaving)

      織入是把切面應用到切點對應的連接點的過程。切面在指定連接點被織入到目標對象中。

具體關係使用圖形化表示是:

2 爲什麼要引入AOP?

上面是定義,引入AOP的原因肯定是可以解決目前開發中存在的某些痛點:

(1)目前的開發當中,相互之間都是模塊化開發,使用AOP可以有效的實現模塊化的思路。

(2)將輔助邏輯(日誌、安全、監控等)從業務主體邏輯中進行剝離,同步進行開發。

AOP是一種思想,這種思想是把一些業務邏輯剝離開,然後按照主業務邏輯進行組合,最後達到想要的功能邏輯。

3 如何簡單的實現AOP?

3.1 AOP編程思路

本圖是一個簡單的思路。

明確需求創建切面類定義切點定義通知思考:要在哪個方法的什麼時候做什麼事(方法前?方法後? 還是around ?)即選擇哪個連接點進行執行通知在切點附近(before?after?) 做什麼操作

下面定義一個日誌的簡單案例,實現AOP。使用的是原始的註解方式:

3.2 案例實現AOP

3.2.1 明確需求

在某個方法上加上@FddLog,就會在執行這個方法的前後,自動輸出相應的信息。下面以把大象放進冰箱爲例子進行演示:

3.2.2 基本接口和實現

public interface ElephentToRe{
  public void toRe();
}

實現類如下:

public class ElephentToReImpl implements ElephentToRe{
  public void toRe() {
    System.out.println("把大象放冰箱");
  }
}

3.2.3 定義切面和通知

public class ElephentToReHelper{
    public void beforeElephentToRe(){
        System.out.println("把冰箱門打開");
    }
    public void afterElephentToRe(){
        System.out.println("把冰箱門關上");
    }
}

配置就好了

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
  <!-- 定義通知內容,也就是切入點執行前後需要做的事情 -->
  <bean id="elephentToReHelper" class="com.fdd.bean.ElephentToReHelper"></bean>
  <!-- 定義被代理者 -->
  <bean id="elephentToReImpl" class="com.fdd.bean.ElephentToReImpl"></bean>
  <aop:config>
    <aop:aspect ref="elephentToReHelper">
      <aop:before method="beforeElephentToRe" pointcut="execution(* *.toRe(..))" />
      <aop:after method="afterElephentToRe" pointcut="execution(* *.toRe(..))" />
    </aop:aspect>
  </aop:config>
</beans>

3.2.4 測試看效果

public class Test {
  public static void main(String[] args){
    @SuppressWarnings("resource")
    ApplicationContext appCtx = new FileSystemXmlApplicationContext("application.xml");
    ElephentToRe elephentToReImpl = (ElephentToRe)appCtx.getBean("elephentToReImpl");
    elephentToReImpl.toRe();
  }
}

上面的這種方法是通過純粹的POJO切面來完成的。實現方式也比較簡單。

4 我對AOP思想的看法

任何新技術的出現都是爲了解決目前開發中存在的某些痛點。對於aop來說,其主要是把一些功能代碼進行抽象封裝,和主業務邏輯代碼進行剝離。在需要的地方進行織入即可。

我的看法是

(1)在平時開發代碼的時候,完全可以把一些常見的,常用的功能代碼進行封裝,儘量做到動態配置。不同的功能模塊只需要進行織入即可。

(2)定義業務邏輯的模板,比如說如果要解決某一個業務功能,如果頁面類似,可以按照基本的框架進行組合,然後使用配置平臺進行可控化配置即可。

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