1. 程式人生 > >學習筆記-Spring Boot 源起-Spring AOP 程式設計

學習筆記-Spring Boot 源起-Spring AOP 程式設計

一、AOP面向方面程式設計

AOP是OOP的延續,是Aspect Oriented Programming的縮寫,意思是面向方面程式設計。OP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是呼叫者和被呼叫者之間的解耦,AOP可以說也是這種目標的一種實現。

AOP帶來了新的程式設計革新。 使得軟體設計更加細化,程式碼的編寫順序不再決定其執行順序,誕生動態元件概念,結合IOC DI模式,可以實現軟體編寫和執行的最大靈活性,自誕生以來已經成為Java領域主流模式,典型框架有Spring或AspectJ框架。

在面向物件系統中,我們經常需要一些任務活動,如記錄,交易的安全性,快取等,這些活動是必要的,但不是業務邏輯的一部分,被稱為"橫切關注點"。

(橫切關注==跨整個系統的常用功能)

從業務邏輯中分離橫切關注點,為寫出一個精心設計的解耦系統邁出了一大步。讓我們關注思考如何處理橫切關注點的分離。

1、繼承Inheritance

繼承的是那些通用功能,繼承需要我們設計一個基類,如果我們要重用到多個地方可能需要修改這個基類。繼承Inheritance == 難以在後來修改(無彈性程式碼)

2、委託Delegation

委託是一個處理橫切關注的好辦法,委託和組合分享一個通用功能,但是我們需要在很多地方呼叫這個委託物件,顯得繁瑣。委託 == 繁瑣

AOP允許我們以模組化關注橫向點並形成物件,稱為Aspect,這樣使用Aspect能夠建立一個乾淨解耦的程式碼。

二、AOP相關概念

1、Concerns關注 – 這是基於功能的模組部分,有兩種型別關注:. 1. 核心關注 2. 跨切面關注(Aspect). 核心關注是有關業務邏輯,比如生成工資單,讓員工記錄,做銀行轉帳。跨切面關注是配合業務的一些活動任務,如日誌 快取等。

2、Joinpoint連線點 – Joinpoint是在執行時的切入點,Aspect也就是跨切面關注的一些功能要加入主要業務功能的地方,一個連線點可以是一個被呼叫的方法。

3、Advice建議 – 每個Aspect都有一個目標,它的任務是什麼,這個任務會被切入到哪個連線點,這些都被稱為Advice. Advice能夠定義Aspect什麼時候執行任務,是在核心關注也就是主要業務活動的之前 之後或者前後執行?

4、Pointcut 切入點– 一個系統有很多連線點,但是並不是所有連線點都需要被選擇切入Aspect的,Aspect從切入點能夠獲得幫助,選擇哪個連線點介入。

5、Aspect方面 – Advice 和 Pointcut定義了一個方面Aspect.Advice定義了Aspect的任務和什麼時候執行它,而切入點Pointcut定義在哪裡具體地方切入,也就是說,Aspect定義了它是什麼東西 什麼時候切入和在哪裡切入。

6、Target目標 – 目標是一個被切入的地方,它一般是核心關注,業務功能。

7、Proxy代理 – 當一個advice應用到目標物件時,這時一個代理物件將被建立. AOP容器建立和管理代理物件的生命週期。

8、Weaving織入 – Weaving是一個混合橫向方面到目標業務物件的過程,織入可以是在編譯時間,也可以在執行時間使用classload,Spring AOP預設是在執行時間。

三、Spring AOP 簡單樣例

Spring 支援 Aspect J 的註解式方面程式設計。

下面通過一個例子展示Spring 的 AOP 的使用。

1、新建maven專案,編輯pom.xml 使之支援Spring AOP

      <!-- 新增spring framework 依賴 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.8.RELEASE</version>
    </dependency>
        
      <!-- spring aop 支援 -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>5.0.8.RELEASE</version>
      </dependency>
      
      <!-- aspectj 支援 -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
          <version>1.9.1</version>
      </dependency>
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.1</version>
      </dependency>

2、編寫攔截規則

/**
 * 
 */
package com.freesky.spring_aop;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 攔截規則的註解
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
    String name();
}

3、編寫使用註解的被攔截類

package com.freesky.spring_aop;

import org.springframework.stereotype.Service;

/**
 * 
 * 使用註解的被攔截類
 *
 */
@Service
public class DemoUseAnnotationService {
    @Action(name="註解式攔截的add操作")
    public void add() {}
}

4、編寫使用方法的被攔截類

package com.freesky.spring_aop;

import org.springframework.stereotype.Service;

/**
 * 
 * 使用方法規則被攔截類
 *
 */
@Service
public class DemoUseMethodService {
    public void add() {}
}


5、編寫切面

package com.freesky.spring_aop;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

/**
 * 
 * 切面類
 *
 */
@Aspect  // 這裡宣告這是一個切面
@Component // 這裡讓此切面成為Spring容器的管理的一個Bean
public class MyAspect {
    
    // 此處宣告切點
    @Pointcut("@annotation(com.freesky.spring_aop.Action)")
    public void annotationPointcut() {};
    
    // 這裡宣告一個建言,並使用 @PointCut 定義的切點
    @After("annotationPointcut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Action action = method.getAnnotation(Action.class);
        // 通過反射獲取註解上的屬性,然後做相關的操作
        System.out.println("註解式攔截" + action.name());
    }
    
    // 宣告一個建言,此建言直接使用攔截規則作為引數
    @Before("execution(* com.freesky.spring_aop.DemoUseMethodService.*(..))")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("方法規則式攔截" + method.getName());
    }

}

6、編寫配置類:

package com.freesky.spring_aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.freesky.spring_aop")
@EnableAspectJAutoProxy // 這裡開啟 Spring 對 AspectJ 代理的支援
public class AopConfig {

}

7、執行測試程式碼

package com.freesky.spring_aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * Spring AOP 測試程式
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
        
        DemoUseAnnotationService demoUseAnnotationService = context.getBean(DemoUseAnnotationService.class);
        
        DemoUseMethodService demoUseMethodService = context.getBean(DemoUseMethodService.class);
        
        demoUseAnnotationService.add();
        demoUseMethodService.add();
        
        context.close();
        
    }
}

四、結束語

通過樣例可以很明顯的看出面向切面(方面)的優勢,相比與OOP,在攔截日誌方面優勢明顯。

關於AOP,更加深入的內容需要繼續學習研究,深刻理解內部原理,就能舉一反三了。

參考:
https://www.jdon.com/aop.html

《JavaEE開發的顛覆者 Spring Boot實戰》