天天看點

Springboot AOP 使用小結引言搭建工程POM依賴添加配置編寫兩個用于測試的接口檔案編寫幾個自定義注解編寫兩個切面配置測試結果

Springboot AOP 使用小結

  • 引言
  • 搭建工程
  • POM依賴
  • 添加配置
  • 編寫兩個用于測試的接口檔案
  • 編寫幾個自定義注解
  • 編寫兩個切面配置
    • 1. 用于注解比對的Aspect
      • 關于注解比對的文法
    • 2. 用于包路徑比對的Aspect
      • 關于包路徑比對的文法簡記
  • 測試結果

引言

為了簡化應用層開發人員的工作複雜性,我一般會把一些通用的,複雜的,跟具體業務沒有特别關系的邏輯封裝在架構層。AOP用的就會比較多。有一段時間,總是會出現自己設計的AOP不能準确比對的問題。特意做了一點研究,為了增強記憶,友善日後應用,做個小結。

搭建工程

搭建一個普通的Springboot工程即可。為了看到效果,我們使用Logger列印日志,配置好日志列印級别。

logging:
    level:
        com.jiezhi.aspect.demo: debug
           

POM依賴

預設的工程沒有aop子產品,要單獨添加。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
           

添加配置

可以在啟動類添加,也可以在專用的配置檔案上面添加。

編寫兩個用于測試的接口檔案

com.jiezhi.aspect.demo.business.user.web.controller.UserController

package com.jiezhi.aspect.demo.business.user.web.controller;

import com.jiezhi.aspect.demo.annotations.OnMethod;
import com.jiezhi.aspect.demo.annotations.OnType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Chris Chan
 * Create on 2021/8/29 17:18
 * Use for: 測試接口
 * Explain:
 */
@OnType
@RestController
@RequestMapping("api/user")
public class UserController {
    @OnMethod
    @GetMapping("test")
    public String test() {
        return "Call api/user/test success.";
    }

    @GetMapping("test1")
    public String test1() {
        return "Call api/user/test1 success.";
    }
}
           

com.jiezhi.aspect.demo.business.worker.web.controller.WorkerController

package com.jiezhi.aspect.demo.business.worker.web.controller;

import com.jiezhi.aspect.demo.annotations.OnMethod;
import com.jiezhi.aspect.demo.annotations.OnType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Chris Chan
 * Create on 2021/8/29 17:18
 * Use for: 測試接口
 * Explain:
 */
@RestController
@RequestMapping("api/worker")
public class WorkerController {
    @OnMethod
    @GetMapping("test")
    public String test() {
        return "Call api/worker/test success.";
    }

    @GetMapping("test1")
    public String test1() {
        return "Call api/worker/test1 success.";
    }
}
           

編寫幾個自定義注解

OnType 用于類上的注解

OnMethod 用于方法上的注解

package com.jiezhi.aspect.demo.annotations;

import java.lang.annotation.*;

/**
 * @author Chris Chan
 * Create on 2021/8/29 17:07
 * Use for:
 * Explain:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OnMethod {
}
           

編寫兩個切面配置

1. 用于注解比對的Aspect

com.jiezhi.aspect.demo.config.AnnotationAspect

package com.jiezhi.aspect.demo.config;

import com.jiezhi.aspect.demo.annotations.OnMethod;
import com.jiezhi.aspect.demo.annotations.OnType;
import org.aspectj.lang.JoinPoint;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author Chris Chan
 * Create on 2021/8/29 17:10
 * Use for: 注解比對切面
 * Explain:
 */
@Component
@Aspect
@Order(0)
public class AnnotationAspect {
    private Logger logger = LoggerFactory.getLogger(AnnotationAspect.class);

    @Pointcut("@within(com.jiezhi.aspect.demo.annotations.OnType)")
    public void cutOnType() {
    }

    @Pointcut("@annotation(com.jiezhi.aspect.demo.annotations.OnMethod)")
    public void cutOnMethod() {
    }

    /**
     * 比對方法上的注解
     *
     * @param joinPoint
     * @param onMethod
     */
    @Before("@annotation(onMethod)")
    public void before(JoinPoint joinPoint, OnMethod onMethod) {
        logger.debug("接口調用OnMethod: {}", joinPoint.getSignature().getName());
    }

    /**
     * 比對類上的注解
     *
     * @param joinPoint
     * @param onType
     */
    @Before("@within(onType)")
    public void before(JoinPoint joinPoint, OnType onType) {
        logger.debug("接口調用OnType: {}", joinPoint.getSignature().getName());
    }

    /**
     * 比對類和方法上的注解
     *
     * @param joinPoint
     */
    @Before("cutOnType() || cutOnMethod()")
    public void before(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();
        OnType onType = targetClass.getDeclaredAnnotation(OnType.class);

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        OnMethod onMethod = method.getDeclaredAnnotation(OnMethod.class);
        logger.debug("接口調用OnType&OnMethod: {} ,OnType: {},OnMethod: {}", ((MethodSignature) joinPoint.getSignature()).getMethod().getName(), onType, onMethod);
    }

}
           

關于注解比對的文法

@annotation(注解名)是一種比較簡便的使用模式,不用定義切點,還可以直接擷取到注解。不過隻能比對方法上面的注解;

@within功能同上,比對的是類上面的注解;

如果要同時比對方法和類上面的注解,不寫切點就不行了,需要參照第三種方式,先寫切點,然後進行邏輯處理。最終需要的注解,要在内部用邏輯自己擷取。

2. 用于包路徑比對的Aspect

com.jiezhi.aspect.demo.config.CommonAspect

package com.jiezhi.aspect.demo.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @author Chris Chan
 * Create on 2021/8/29 17:10
 * Use for: 包名比對切面
 * Explain:
 */
@Component
@Aspect
@Order(1)
public class CommonAspect {
    private Logger logger = LoggerFactory.getLogger(CommonAspect.class);

    /**
     * 第一個參數是權限修飾符比對
     * 第二個參數是傳回值類型比對
     * 第三部分(最後一段之前)是包路徑比對 *表示任意包
     * 第四部分(最後一段)是類比對 *表示所有類
     * 第五部分(括号内)是參數清單比對 ..表示所有參數
     * ..表示比對多重
     */
    @Pointcut("execution(public * com.jiezhi.aspect.demo.business.*.web.controller.*Controller.*(..))")
    public void cutController() {
    }
    @Pointcut("execution(public * com.jiezhi.aspect.demo..*Control*.*(..))")
    public void cutController1() {
    }
    /**
     * 比對Controller
     *
     * @param joinPoint
     */
    @Before("cutController1()")
    public void before(JoinPoint joinPoint) {
        logger.debug("接口調用 路徑比對1: {}", joinPoint.getSignature().getName());
    }

}

           

關于包路徑比對的文法簡記

包路徑最後一節是類名,*比對任何類,同時*也可以在類名中比對任何字元串;

..比對任意多段路徑;

括号内是參數比對,..比對任意參數表,具體細節所用較少,暫時沒有過細研究。

測試結果

各種情況都測試過,符合預期效果。達成此種結果,基本上就可以靈活運用了。

Springboot AOP 使用小結引言搭建工程POM依賴添加配置編寫兩個用于測試的接口檔案編寫幾個自定義注解編寫兩個切面配置測試結果

繼續閱讀