1. 程式人生 > 程式設計 >Spring Cloud Alibaba-AOP(十九)

Spring Cloud Alibaba-AOP(十九)

簡介

AOP(面向切面程式設計),作為OOP(面向物件程式設計)的補充,用於處理哪些業務無關的,例如鑑權,日誌等公共邏輯,將之抽取封裝成一個可重用的模組(切面),減少程式碼重複,降低耦合,提高系統可維護性。

方式

靜態代理

編譯階段將AspectJ(切面)織入到Java位元組碼生成AOP代理類

動態代理

執行階段在記憶體中臨時生產一個AOP物件且在特定的切點做了增強處理

  • JDK動態代理(基於介面)
  • CGLIB動態代理(基於繼承)

術語

  • Advice(通知/增強):切面需要做的具體工作
  • Join point(連線點):允許使用通知的地方
  • Poincut(切點):執行的位置
  • Aspect(切面):通知和切點的結合
  • Introduction(引入):切面定義的屬性方法應用到目標類
  • target(目標):被通知的物件
  • Weaving(織入):把切面加入程式程式碼的過程

常用的增強型別

  • 前置通知(before):在某連線點之前執行的通知,但這個通知不能阻止連線點前的執行(除非丟擲異常)
  • 返回後通知(afterReturning):在某連線點正常完成後執行的通知
  • 丟擲異常後通知(afterThrowing):在方法丟擲異常退出時執行的通知
  • 後通知(after):當某連線點退出的時候執行的通知
  • 環繞通知(around):包圍一個連線點通知

執行順序

  • @Order(1):越小越先執行
  • around->before->around->after->afterReturning
  • 橙色:@Order(1),綠色:@Order(2)

Springboot引入AOP

  • 加依賴
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
複製程式碼
  • 寫註解
複製程式碼
  • 寫配置
複製程式碼

Introduction

package com.virgo.user.auto;

import com.virgo.user.service.TestService;
import com.virgo.user.service.TestServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/24 下午1:00
 */
@Aspect
@Component
public class IntroductionAop {
    @DeclareParents(value = "com.virgo.user..service..*"
,defaultImpl = TestServiceImpl.class) public TestService testService; } package com.virgo.user.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; /** * @author zhaozha * @date 2019/10/24 下午1:02 */ @Service @Slf4j public class TestServiceImpl implements TestService{ @Override public void test() { log.info("all can use"); } } ... // CommonService使用TestService TestService testService = (TestService)commonServiceImpl; testService.test(); ... 複製程式碼

順序

  • 程式碼
package com.virgo.user.auto;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/24 下午1:29
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class TestAopOrder1 {
    @Pointcut("execution(* com.virgo.user.service.*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void begin() {
        log.info("2:{}","before");
    }

    @After("pointcut()")
    public void commit() {
        log.info("9:{}","after");
    }

    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("10:{}","afterReturning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        log.info("afterThrowing");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            log.info("1:{}","around");
            return joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        } finally {
            log.info("8:{}","around");
        }
    }
}
package com.virgo.user.auto;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/24 下午1:11
 */
@Slf4j
@Aspect
@Component
@Order(2)
public class TestAopOrder2 {
    @Pointcut("execution(* com.virgo.user.service.*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void begin() {
        log.info("4:{}","before");
    }

    @After("pointcut()")
    public void commit() {
        log.info("6:{}","after");
    }

    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        log.info("7:{}","afterReturning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        log.info("afterThrowing");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            log.info("3:{}","around");
            return joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        } finally {
            log.info("5:{}","around");
        }
    }
}
複製程式碼
  • 效果

註解生效AOP

註解

  • @Target:註解的作用目標
    • ElementType.TYPE:允許被修飾的註解作用在類、介面和列舉上
    • ElementType.FIELD:允許作用在屬性欄位上
    • ElementType.METHOD:允許作用在方法上
    • ElementType.PARAMETER:允許作用在方法引數上
    • ElementType.CONSTRUCTOR:允許作用在構造器上
    • ElementType.LOCAL_VARIABLE:允許作用在本地區域性變數上
    • ElementType.ANNOTATION_TYPE:允許作用在註解上
    • ElementType.PACKAGE:允許作用在包上
  • @Retention:註解的生命週期
    • RetentionPolicy.SOURCE:Annotations are to be discarded by the compiler.(編譯期可見,不會寫入 class 檔案)
    • RetentionPolicy.CLASS:Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time. This is the default behavior.(寫入 class 檔案,類載入丟棄)
    • RetentionPolicy.RUNTIME:Annotations are to be recorded in the class file by the compiler and retained by the VM at run time,so they may be read reflectively.(永久儲存,可以反射獲取)
  • @Documented:註解是否應當被包含在 JavaDoc 檔案中
  • @Inherited:是否允許子類繼承該註解

AOP

  • 程式碼
package com.virgo.user.auto;

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

/**
 * @author zhaozha
 * @date 2019/10/24 下午1:39
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAop {
    String value() default "";
}

package com.virgo.user.auto;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author zhaozha
 * @date 2019/10/24 下午1:53
 */
@Slf4j
@Aspect
@Component
@Order(1)
public class TestAnnotationAop {
    @Pointcut(value = "@annotation(logAop)",argNames = "logAop")
    public void pointcut(LogAop logAop) {
    }

    @Around(value = "pointcut(logAop)",argNames = "joinPoint,logAop")
    public Object around(ProceedingJoinPoint joinPoint,LogAop logAop) throws Throwable {
        try {
            log.info(logAop.value());
            return joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        } finally {
            log.info("");
        }
    }
}
複製程式碼
  • 效果(方法加註釋@LogAop("測試Annotation"))

execution

execution(* com.virgo.user.service...(..))

  • 第一個*:任意的返回值
  • com.virgo.user.service:包名
  • .. :當前包及其子包
  • *:所有類
  • .*(..) 表示任何方法,括號代表引數 .. 表示任意引數