4、spring核心AOP

項羽齊發表於2018-03-27

 

Spring AOP

 

1. Spring AOP 概述 1-1

1.1. 何為AOP 1-1

1.2. AOP要解決什麼問題? 1-1

1.3. AOP實際專案應用場景? 1-2

1.4. AOP底層原理實現分析? 1-2

2. Spring AOP 程式設計實現 2-2

2.1. AOP 基本步驟 2-2

2.2. AOP 基於xml實現 2-2

2.2.1. 建立專案新增依賴 2-3

2.2.2. 新增spring配置檔案 2-3

2.2.3. 建立核心業務類 2-4

2.2.4. 建立日誌處理類 2-4

2.2.5. 配置bean物件 2-5

2.2.6. 編寫測試類 2-6

3. 總結 3-7

3.1. 重點和難點分析 3-7

3.2. 常見FAQ 3-7

 

1. Spring AOP 概述

  1.1. 何為AOP

    AOP 是軟體設計領域中的面向切面程式設計,它是物件導向程式設計的一種補充和完善實際專案中我們通常將物件導向理解為一個靜態過程(例如一個系統有多少模組,一個模組有哪些物件,物件有哪些屬性),面向切面理解為一個動態過程(在物件執行時動態織入一些功能。)

 

 

  1.2. AOP要解決什麼問題?

    實際專案中通常會將系統兩大部分:核心關注點和非核心關注點

    思考?

    程式設計過程中首先要完成的是什麼?核心關注點(核心業務)

    非核心關注點如何切入到系統中?硬編碼(違背OCP)AOP(推薦)

 

    AOP就是要在基於OCP在不改變原有系統核心業務程式碼的基礎上動態新增一些擴充套件功能。

 

  1.3. AOP實際專案應用場景?

 

    AOP 通常應用於日誌的處理,事務處理,許可權處理,快取處理等等。

  

 

  1.4. AOP底層原理實現分析?

     AOP底層基於代理機制實現功能擴充套件:(瞭解)

      1) 假如目標物件(被代理物件)實現介面,則底層預設採用JDK動態代理機制為目標物件建立代理物件(目標類和代理類會實現共同介面)

      2) 假如目標物件(被代理物件)沒有實現介面,則底層預設採用CGLIB代理機制為目標物件建立代理物件(預設建立的代理類會繼承目標物件型別)。

 

  2. Spring AOP 程式設計實現

     2.1. AOP 基本步驟

       step1:建立maven java 專案

      step2:新增aop依賴

      step3:配置aop 核心(基於xml,基於註解)

      step4:定義核心業務(核心關注點):推薦先寫介面再寫實現類

      step5:定義擴充套件業務(非核心關注點)

      step6:基於配置實現非核心業務的切入

      step7:編寫測試類進行單元測試

 

  2.2. AOP 基於xml實現

     通過AOP為核心業務類新增日誌處理

    2.2.1. 建立專案新增依賴

    建立maven java 專案然後新增依賴

     2.2.2. 新增spring配置檔案

 

 

    2.2.3. 建立核心業務類

    建立介面

  

public interface HelloService {
     void sayHello(String msg);
}

 

建立介面實現類

public class HelloServiceImpl implements HelloService {
    public void sayHello(String msg) {
        //假設這條語句是我們系統中的核心業務
        System.out.println(msg);
    }
}

2.2.4. 建立日誌處理類

 

public class LoggingAspect {
     public void beforeMethod(){
         System.out.println("method start");
     }
     public void afterMethod(){
         System.out.println("method end");
     }
}

 

2.2.5. 配置bean物件

 

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   
    <!-- 核心業務物件 -->
    <bean id="helloService"
          class="spring.beans.HelloServiceImpl"/>
    <!-- 配置非核心業務物件(日誌處理物件):切面 -->
    <bean id="log" 
          class="spring.aop.LoggingAspect"/>
    <!-- AOP配置(切入點,切面) -->  
    <aop:config>
       <!-- 配置切入點 -->
       <aop:pointcut 
            expression="within(spring.beans.HelloServiceImpl)" 
            id="logPointCut"/>
       <!-- 配置日誌處理 -->
       <aop:aspect ref="log" >
           <aop:before method="beforeMethod" 
                       pointcut-ref="logPointCut"/>
           <aop:after  method="afterMethod"
                       pointcut-ref="logPointCut"/>
       </aop:aspect>
    </aop:config>
</beans>

 

2.2.6. 編寫測試類

 

//1.初始化spring容器
        ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        //2.獲取Bean物件
        HelloService helloService=
        ctx.getBean("helloService",HelloService.class);
        //3.執行業務
        helloService.sayHello("CGB1710");//proxy的方法
        //4.釋放資源
        ctx.close();

 

 

2.3. AOP 基於註解實現

先參考AOP參考文件進行整理,後續上傳 

  1.1.1. 建立maven 專案新增依賴

    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.3.9.RELEASE</version>
       </dependency>
        <!--
        Spring AOP的實現依託於Aspect框架
        所以要引用1.8.5有問題
        -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

 

 

 

 

  1.1.2. 建立核心業務類

    定義一個訂單介面,此介面中定義多個業務操作。

 

public interface OrderService {
     public void saveOrder();
     public void deleteOrder();
}

 

  1.1.3. 建立時間處理類

    將此時間處理類作為核心業務增強(一個橫切面物件)類,用於輸出業務開始執行時間,以及業務結束執行時間。

    橫切面物件主要由兩部分構成:切入點(用於@Pointcut標識),以及功能增強(用通知@Before@After等進行標識)

 

@Aspect
@Service
public class TimeAspect {
    @Pointcut("bean(orderService)")
    public void pointcut(){}
    /**增強功能:前置通知(業務方法執行之前執行)*/
    @Before("pointcut()")
    public void begin(){
      System.out.println("start:"+System.nanoTime());
    }
    /**增強功能:最終通知(業務方法執行最後執行,
     *無論業務方法是否執行成功,此功能都要執行)*/
    @After("pointcut()")
    public void end(){
      System.out.println("end:"+System.nanoTime());
    }
}

 

    其中:

      @Aspect 註解用於標識此類為一個AOP橫切面物件

      @Pointcut 註解用於定義本類中的切入點,本案例中切入點表示式用的是bean表示式,這個表示式以bean開頭,bean括號中的內容為一個spring管理的某個bean物件的id

      @Before 用於定義一個前置通知(滿足切入點表示式的核心業務方法執行之前要執行的一個操作)

      @After  用於定義一個後置通知(滿足切入點表示式的核心業務方法執行之後要執行的一個操作)

 

    術語增強:

      切面:用於封裝擴充套件業務的一個類的物件。

      通知:切面擴充套件業務中的一個操作。

 

  1.1.4. 配置AOP實現

    對於基於註解方式的配置一般有兩種方式,一種是直接在xml核心配置檔案中進行配置,還有一種在類中基於註解進行配置。例如

     基於xml方式配置對註解的應用

 

<?xml version="1.0" encoding="UTF-8"?>
<beans 
    default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd"> 
   
       <!-- 配置對類元件的掃描 -->
       <context:component-scan 
       base-package="com.spring"/>
       <!-- 啟用AOP註解(自動為目標物件建立代理物件) -->
       <aop:aspectj-autoproxy/>

</beans>

 

 

  在類中基於註解方式的配置

 

@ComponentScan("com.spring.beans")
@EnableAspectJAutoProxy
public class AppConfig {

}

 

  1.1.5. 編寫測試類

 

public class TestAOP01 {

    public static void main(String[] args) {
        //1.初始化容器
        ClassPathXmlApplicationContext ctx=
        new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        //2.獲取bean物件
        OrderService os=(OrderService)
        ctx.getBean("orderService", 
                OrderService.class);
        //3.執行業務方法
        os.saveOrder();
        os.deleteOrder();
        //4.釋放資源
        ctx.close();
    }
}

 

    基於類中註解方式配置的測試實現

 

public class TestAOP02 {
    public static void main(String[] args) {
        //1.初始化容器物件
        AnnotationConfigApplicationContext ctx=
        new AnnotationConfigApplicationContext(
                AppConfig.class);
        //2.獲取Bean物件
        OrderService orderService=
        ctx.getBean("orderService", OrderService.class);
        //3.執行業務
        orderService.saveOrder();
        //orderService.deleteOrder();
        //4.釋放資源
        ctx.close();
    }
}

 

 

      基於類的註解配置初始化工廠時需要初始化

      AnnotationConfigApplicationContext物件

 

  2. Spring AOP 程式設計增強

    2.1. 切面表示式增強

 

        Spring中通過切入點表示式定義具體切入點,其常用AOP切入點表示式定義及說明:

 

 

指示符

作用

 

bean

用於匹配指定bean id的的方法執行

 

within

用於匹配指定包名下型別內的方法執行

 

execution

用於進行細粒度方法匹配執行具體業務

 

    2.1.1. Bean表示式應用增強

         bean應用於類級別,實現粗粒度的控制:

 

bean(UserServiceImpl))

指定一個類

bean(*Service)

指定所有的字尾為service的類

 

 

    2.1.2. Within表示式應用增強

         within應用於類級別,實現粗粒度的控制:

within(aop.service.UserServiceImpl))

指定類,只能指定一個類

within(aop.service.*))

只包括當前目錄下的類

within(aop.service..*))

指定當前目錄包含所有子目錄中的類

 

 

    2.1.3. Execution表示式應用增強

        execution方法級別,細粒度的控制:

        語法:execution(返回值型別 包名.類名.方法名(引數列表))

execution(void aop.service.UserServiceImpl.addUser())

匹配方法

execution(void aop.service.PersonServiceImpl.addUser(String))

方法引數必須為字串

execution(* aop.service..*.*(..))

萬能配置

 

 

     2.2. 切面通知增強

      在AOP程式設計中有五種型別的通知:

        1) 前置通知

        2) 後置通知

        3) 異常通知

        4) 最終通知

        5) 環繞通知

 

    2.2.1. Xml方式通知配置增強

      切入點及前置通知,後置通知,返回通知,異常通知,環繞通知的配置

 

<aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:before method="beforeMethod" pointcut-ref="pc"/>
            <aop:after method="afterMethod" pointcut-ref="pc"/>
            <aop:after-returning method="returnMethod"
 pointcut-ref="pc"/>
            <aop:after-throwing method="throwMethod" 
pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

  

切入點及環繞通知的配置(瞭解)
    <aop:config>
        <aop:pointcut id="pc"expression="execution(* 
com.company.spring.service..*.*(..))" >
        <aop:aspect ref="loggingAspect">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

 

2.2.2. 註解方式通知配置增強

切入點及前置通知,後置通知,返回通知,異常通知,環繞通知的配置

 

@Aspect
@Service
public class TxAspect {
    @Before("bean(orderService)")
    public void startTx(){
        System.out.println("開啟事務");
    }
    @AfterReturning("bean(orderService)")
    public void commitTx(){
        System.out.println("提交事務");
    }
    @AfterThrowing("bean(orderService)")
    public void rollbackTx(){
        System.out.println("回滾事務");
    }
    @After("bean(orderService)")
    public void closeResource(){
        System.out.println("釋放資源");
    }
}

 

 

切入點及環繞通知的配置

 

@Component
@Aspect
public class TxManager {
    @Pointcut("execution(com.company.spring.service..*.*(..))")
    public void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) 
            throws Throwable{
            System.out.println("事務開始");
            Object result = joinPoint.proceed();
            System.out.println("事務結束");
            return result;
    }
}

 

2.3. 切面執行順序配置增強

實際專案中可能會有多個切面,切面之間的執行可能需要一定的順序

2.3.1. Xml方式配置執行順序

 

<aop:config>
        <aop:pointcut id="pc"
             expression="execution(* 
com.company.spring.service..*.*(..))"/>
        <aop:aspect ref="loggingAspect" order="1">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
        <aop:aspect ref="txManager" order="2">
            <aop:around method="aroundMethod" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>

 

2.3.2. 註解方式配置執行順序

註解方式順序配置需要藉助@Order註解

 

@Order(1)
@Aspect
@Component
public class TxManager {
    @Pointcut("execution(* com.company.spring.service..*.(..))")
    public void pointCut() {}
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) 
            throws Throwable{
            System.out.println("事務開始");
            Object result = joinPoint.proceed();
            System.out.println("事務結束");
            return result;
    }
}

 

註解方式順序配置

@Order(2)
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* com.company.spring.service..*.(..))")
    public void pointCut() {}
    @Before("pointCut()")
    public void beforeMethod() {
        System.out.println("beforeMethod");
    }
    @Before("pointCut()")
    public void afterMethod() {
        System.out.println("afterMethod");
    }
}

 

  3. 總結

    3.1. 重點和難點分析

  1. AOP 是什麼,解決了什麼問題,實現原理,應用場景
  2. AOP 程式設計基本步驟及基本實現
  3. AOP 程式設計中重點掌握基於註解方式配置

 

    3.2. 常見FAQ

  1. 什麼是OCP原則(開閉原則)
  2. 什麼是單一職責原則(一個類或介面的職責不要太多)
  3. springAOP的有哪些配置方式(xml和註解)
  4. Spring AOP 的通知有哪些基本型別
  5. Spring AOP是如何為Bean物件建立代理物件的

 

3.3. 作業

  1. 總結課堂知識點(筆記再詳細也是別人的,不是自己的,拿來主義對自己的提高會很慢,無論是否認同,可以試試,看看最後效果)
  2. 完成課堂AOP基本案例
  3. 瞭解代理模式及應用場景,實現方式(最好自己嘗試實現)

 

相關文章