欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實(shí)現(xiàn)AOP日志切面功能的詳細(xì)教程

 更新時(shí)間:2025年07月09日 10:31:48   作者:Anfioo  
AOP是Spring框架的核心特性之一,常用于日志記錄、權(quán)限校驗(yàn)、事務(wù)管理等場(chǎng)景,本文將手把手教你如何在Spring Boot項(xiàng)目中實(shí)現(xiàn)AOP日志切面功能,包括依賴引入、切點(diǎn)定義、切面實(shí)現(xiàn)、注解自定義等內(nèi)容,需要的朋友可以參考下

Spring Boot 實(shí)現(xiàn)AOP日志切面全流程教程

效果

切入com.anfioo下的所有controller層

切入com.anfioo下的所有service層

切入自定義注解@LogRecord

全都開(kāi)啟

可以使用aop切片更好的打印這個(gè)方法信息,而不污染原有的方法

一、引入依賴

首先,確保你的Spring Boot項(xiàng)目已經(jīng)引入了AOP相關(guān)依賴。Spring Boot Starter通常已經(jīng)包含了AOP依賴,但你可以在pom.xml中顯式添加:

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

二、配置AOP屬性(可選)

為了靈活控制切面的開(kāi)啟與關(guān)閉,我們可以通過(guò)配置文件添加自定義屬性。例如:

# application.yaml
aop:
  debug:
    controller: true   # 控制Controller切面是否開(kāi)啟
    service: false     # 控制Service切面是否開(kāi)啟
    log-record: true   # 控制LogRecord注解切面是否開(kāi)啟

并通過(guò)@ConfigurationProperties將其綁定到Java類:

@Data
@Component
@ConfigurationProperties(prefix = "aop.debug")
public class AopDebugProperties {
    private Boolean controller = true;
    private Boolean service = false;
    private Boolean logRecord = true;
}

三、定義切點(diǎn)(Pointcut)

切點(diǎn)用于指定哪些類或方法會(huì)被AOP攔截。常見(jiàn)的切點(diǎn)表達(dá)式有:

  • execution(public * com.example..controller..*(..)):攔截所有controller包下的公共方法
  • @annotation(com.example.LogRecord):攔截所有被自定義注解標(biāo)記的方法

四、實(shí)現(xiàn)切面(Aspect)

以Controller層日志為例,實(shí)現(xiàn)一個(gè)切面類:

package com.anfioo.common.log;

import com.anfioo.common.bean.AopDebugProperties;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 全局 Controller 層日志記錄切面
 */
@Aspect
@Component
@Slf4j
public class ControllerLogAspect {
    @Autowired
    private AopDebugProperties aopDebugProperties;

    /**
     * 定義切點(diǎn),匹配 com.anfioo 包下所有子包中的 controller 類的公共方法
     */
    @Pointcut("execution(public * com.anfioo..controller..*(..))")
    public void controllerMethods() {
    }

    /**
     * 環(huán)繞通知,用于記錄 controller 層方法的請(qǐng)求和響應(yīng)信息
     *
     * @param joinPoint 切入點(diǎn)對(duì)象,包含被攔截方法的信息
     * @return 被攔截方法的執(zhí)行結(jié)果
     * @throws Throwable 如果執(zhí)行過(guò)程中出現(xiàn)異常
     */
    @Around("controllerMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        if (Boolean.FALSE.equals(aopDebugProperties.getController())) {
            // 不開(kāi)啟,直接執(zhí)行原方法
            return joinPoint.proceed();
        }

        // 記錄開(kāi)始時(shí)間
        long start = System.currentTimeMillis();
        // 獲取目標(biāo)類的 Class 對(duì)象
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 獲取目標(biāo)類的 Logger 對(duì)象
        Logger logger = LoggerFactory.getLogger(targetClass);

        // 獲取當(dāng)前請(qǐng)求的 HttpServletRequest 對(duì)象
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 提取請(qǐng)求的 URL、方法、IP 地址、處理方法和參數(shù)
        String url = request.getRequestURL().toString();
        String method = request.getMethod();
        String ip = request.getRemoteAddr();
        String classMethod = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();


        String red = "\u001B[31m"; // ANSI 紅色
        String line = IntStream.range(0, 200)  // 200 可以改成任意長(zhǎng)度
                .mapToObj(i -> "#")
                .collect(Collectors.joining());
        System.out.println(red + line);


        // 記錄請(qǐng)求信息
        logger.info("\n====== ?? 請(qǐng)求信息 ======\n" +
                        "?? URL        : {}\n" +
                        "?? Method     : {}\n" +
                        "?? IP         : {}\n" +
                        "?? Handler    : {}\n" +
                        "?? Parameters : {}\n" +
                        "==========================",
                url, method, ip, classMethod, Arrays.toString(args));

        // 嘗試執(zhí)行目標(biāo)方法,并記錄執(zhí)行結(jié)果或異常信息
        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            // 記錄異常信息,并重新拋出異常
            logger.error("\n====== ?? ? 異常信息 ======\n" +
                            "?? Handler    : {}\n" +
                            "?? Error      : {}\n" +
                            "==========================",
                    classMethod, e.getMessage(), e);
            throw e;
        }

        // 記錄結(jié)束時(shí)間,并計(jì)算耗時(shí)
        long end = System.currentTimeMillis();
        // 記錄響應(yīng)信息
        logger.info("\n====== ?? ? 響應(yīng)信息 ======\n" +
                        "?? Handler    : {}\n" +
                        "?? Response   : {}\n" +
                        "?? 耗時(shí)        : {} ms\n" +
                        "==========================",
                classMethod, result, end - start);

        return result;
    }
}

說(shuō)明

  • @Aspect:聲明該類為切面
  • @Pointcut:定義切點(diǎn)
  • @Around:環(huán)繞通知,可在方法執(zhí)行前后插入邏輯
  • ProceedingJoinPoint:用于獲取方法信息、參數(shù)、執(zhí)行目標(biāo)方法等

五、自定義注解與切面

有時(shí)我們希望只對(duì)特定方法記錄日志,可以自定義注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRecord {
    String value() default "";
}

并實(shí)現(xiàn)對(duì)應(yīng)的切面:

package com.anfioo.common.log;

import com.anfioo.common.bean.AopDebugProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 記錄 @LogRecord 注解標(biāo)記的方法調(diào)用日志
 */
@Aspect
@Component
@Slf4j
public class LogRecordAspect {

    @Autowired
    private AopDebugProperties aopDebugProperties;

    /**
     * 切點(diǎn):攔截所有帶有 @LogRecord 注解的方法
     */
    @Pointcut("@annotation(com.anfioo.common.log.LogRecord)")
    public void logRecordMethods() {
    }

    /**
     * 環(huán)繞通知:記錄方法執(zhí)行詳情
     */
    @Around("logRecordMethods() && @annotation(logRecord)")
    public Object around(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable {
        if (Boolean.FALSE.equals(aopDebugProperties.getLogRecord())) {
            return joinPoint.proceed(); // 不開(kāi)啟則跳過(guò)
        }

        long start = System.currentTimeMillis();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Logger logger = LoggerFactory.getLogger(targetClass);

        String description = logRecord.value(); // 注解中的描述信息
        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();

        logger.info("\n====== ?? LogRecord 方法調(diào)用 ======\n" +
                        "?? 描述        : {}\n" +
                        "?? 方法        : {}\n" +
                        "?? 參數(shù)        : {}\n" +
                        "=====================================",
                description, methodName, Arrays.toString(args));

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            logger.error("\n====== ??? LogRecord 異常 ======\n" +
                            "?? 方法        : {}\n" +
                            "?? 異常信息     : {}\n" +
                            "=====================================",
                    methodName, e.getMessage(), e);
            throw e;
        }

        long end = System.currentTimeMillis();
        logger.info("\n====== ??? LogRecord 返回結(jié)果 ======\n" +
                        "?? 方法        : {}\n" +
                        "?? 返回值      : {}\n" +
                        "?? 耗時(shí)        : {} ms\n" +
                        "=====================================",
                methodName, result, end - start);

        return result;
    }
}

六、Service層切面實(shí)現(xiàn)

同理,可以為Service層實(shí)現(xiàn)切面:

package com.anfioo.common.log;

import com.anfioo.common.bean.AopDebugProperties;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Service 層方法日志切面(記錄業(yè)務(wù)邏輯調(diào)用)
 */
@Aspect
@Component
@Slf4j
public class ServiceLogAspect {

    @Autowired
    private AopDebugProperties aopDebugProperties;


    /**
     * 切入 com.anfioo 包下所有 service 的方法
     */
    @Pointcut("execution(* com.anfioo..service..*(..))")
    public void serviceMethods() {
    }

    /**
     * 環(huán)繞通知記錄 service 層方法的調(diào)用情況
     */
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        if (Boolean.FALSE.equals(aopDebugProperties.getService())) {
            // 不開(kāi)啟,直接執(zhí)行原方法
            return joinPoint.proceed();
        }


        long start = System.currentTimeMillis();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Logger logger = LoggerFactory.getLogger(targetClass);

        String methodName = joinPoint.getSignature().toShortString();
        Object[] args = joinPoint.getArgs();

        logger.info("\n====== ?? Service 方法調(diào)用 ======\n" +
                    "?? Method     : {}\n" +
                    "?? Parameters : {}\n" +
                    "===============================",
                methodName, Arrays.toString(args));

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            logger.error("\n====== ?? ? Service 異常 ======\n" +
                         "?? Method     : {}\n" +
                         "?? Error      : {}\n" +
                         "=============================",
                    methodName, e.getMessage(), e);
            throw e;
        }

        long end = System.currentTimeMillis();
        logger.info("\n====== ?? ? Service 返回結(jié)果 ======\n" +
                    "?? Method     : {}\n" +
                    "?? Result     : {}\n" +
                    "?? 耗時(shí)        : {} ms\n" +
                    "===============================",
                methodName, result, end - start);

        return result;
    }
}

七、完整流程總結(jié)

  1. 引入依賴:確保spring-boot-starter-aop已添加
  2. 配置屬性:通過(guò)配置文件靈活控制切面開(kāi)關(guān)
  3. 定義切點(diǎn):用表達(dá)式或注解指定攔截范圍
  4. 實(shí)現(xiàn)切面:用@Aspect、@Around等實(shí)現(xiàn)日志邏輯
  5. 自定義注解:實(shí)現(xiàn)更細(xì)粒度的日志控制
  6. 應(yīng)用注解:在需要記錄日志的方法上加上自定義注解

八、常見(jiàn)問(wèn)題

  • 切面不生效? 檢查@Component、@Aspect是否加上,切點(diǎn)表達(dá)式是否正確,AOP依賴是否引入。
  • 日志打印不全? 檢查日志級(jí)別、切面邏輯是否被條件限制跳過(guò)。

AOP 的主要好處

關(guān)注點(diǎn)分離(Separation of Concerns)

AOP 允許你將與業(yè)務(wù)邏輯無(wú)關(guān)的“橫切關(guān)注點(diǎn)”(如日志記錄、安全控制、異常處理、事務(wù)管理)從核心業(yè)務(wù)代碼中分離出去,使業(yè)務(wù)邏輯更專注、更清晰。

避免重復(fù)代碼,提高可維護(hù)性

常見(jiàn)的重復(fù)操作如打印日志、性能統(tǒng)計(jì)、權(quán)限校驗(yàn),如果分散在各個(gè)方法中,會(huì)導(dǎo)致維護(hù)困難。AOP 將這些重復(fù)邏輯集中在一個(gè)地方,修改一次即可生效全局。

不侵入業(yè)務(wù)代碼

通過(guò) AOP,你可以在不修改原方法的前提下,增強(qiáng)其功能(如記錄參數(shù)、返回值、異常信息等),實(shí)現(xiàn)“開(kāi)閉原則”:對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉。

增強(qiáng)系統(tǒng)的可觀測(cè)性與調(diào)試能力

配合 AOP 自動(dòng)記錄方法調(diào)用軌跡、執(zhí)行耗時(shí)、輸入輸出信息,極大提升問(wèn)題排查和性能分析的效率。

靈活可配置

結(jié)合注解、自定義屬性、開(kāi)關(guān)控制(如 AopDebugProperties),你可以根據(jù)環(huán)境或條件動(dòng)態(tài)啟用/禁用某些切面邏輯,適應(yīng)多種部署或調(diào)試場(chǎng)景。

提高開(kāi)發(fā)效率

開(kāi)發(fā)人員無(wú)需手動(dòng)添加日志、異常處理等模板式代碼,只需專注于業(yè)務(wù)邏輯,其余交由統(tǒng)一切面處理,顯著提高開(kāi)發(fā)效率和代碼一致性。

總結(jié)

AOP 幫你在“不碰業(yè)務(wù)代碼”的前提下,實(shí)現(xiàn)系統(tǒng)級(jí)增強(qiáng),讓代碼更干凈、功能更強(qiáng)大、維護(hù)更輕松。

通過(guò)AOP切面,你可以優(yōu)雅地實(shí)現(xiàn)日志記錄、權(quán)限校驗(yàn)等橫切關(guān)注點(diǎn),極大提升代碼的可維護(hù)性和可擴(kuò)展性。希望本文能幫助你快速上手Spring Boot的AOP切面開(kāi)發(fā)!

以上就是SpringBoot實(shí)現(xiàn)AOP日志切面功能的詳細(xì)教程的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot AOP日志切面的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java語(yǔ)言實(shí)現(xiàn)非遞歸實(shí)現(xiàn)樹(shù)的前中后序遍歷總結(jié)

    Java語(yǔ)言實(shí)現(xiàn)非遞歸實(shí)現(xiàn)樹(shù)的前中后序遍歷總結(jié)

    今天小編就為大家分享一篇關(guān)于Java語(yǔ)言實(shí)現(xiàn)非遞歸實(shí)現(xiàn)樹(shù)的前中后序遍歷總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • SpringMVC中處理Http請(qǐng)求的原理詳解

    SpringMVC中處理Http請(qǐng)求的原理詳解

    這篇文章主要介紹了SpringMVC中處理Http請(qǐng)求的原理詳解,當(dāng)一個(gè)http請(qǐng)求過(guò)來(lái)了首先經(jīng)過(guò)的是DispatcherServlet這么一個(gè)前端控制器并調(diào)用了這個(gè)前端控制器的doService方法,這個(gè)方法最終我們發(fā)現(xiàn)它調(diào)用了doDispatcher這么一個(gè)方法,需要的朋友可以參考下
    2023-12-12
  • 對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯的方案

    對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯的方案

    最近項(xiàng)目要求部署到其他公司的服務(wù)器上,但是又不想將源碼泄露出去,要求對(duì)正式環(huán)境的啟動(dòng)包進(jìn)行安全性處理,防止客戶直接通過(guò)反編譯工具將代碼反編譯出來(lái),本文介紹了如何對(duì)SpringBoot項(xiàng)目Jar包進(jìn)行加密防止反編譯,需要的朋友可以參考下
    2024-08-08
  • java反射獲取包下所有類的操作

    java反射獲取包下所有類的操作

    這篇文章主要介紹了java反射獲取包下所有類的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • IDEA中try catch拋異常快捷鍵分享

    IDEA中try catch拋異常快捷鍵分享

    在編寫(xiě)Java代碼時(shí),使用IDEA的快捷鍵CTRL+ALT+t可以快速生成try..catch語(yǔ)句塊,有效提高編碼效率,首先選擇需要處理的代碼片段,然后按下快捷鍵,選擇try/catch選項(xiàng),即可自動(dòng)包圍選中代碼,這一快捷操作簡(jiǎn)化了異常處理步驟,減少了手動(dòng)編寫(xiě)的時(shí)間,是編程中的實(shí)用技巧
    2024-10-10
  • java獲取本地文件和遠(yuǎn)程文件的方式代碼示例

    java獲取本地文件和遠(yuǎn)程文件的方式代碼示例

    這篇文章主要給大家介紹了關(guān)于java獲取本地文件和遠(yuǎn)程文件的方式,我們項(xiàng)目開(kāi)發(fā)的時(shí)候,經(jīng)常會(huì)讀取文件,如果文件在本服務(wù)器,則直接用new File()讀取即可,但是有時(shí)候需要遠(yuǎn)程讀取文件,需要的朋友可以參考下
    2023-08-08
  • springboot中的controller參數(shù)映射問(wèn)題小結(jié)

    springboot中的controller參數(shù)映射問(wèn)題小結(jié)

    這篇文章主要介紹了springboot中的controller參數(shù)映射問(wèn)題小結(jié),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • Java8中流的性能及流的幾個(gè)特性

    Java8中流的性能及流的幾個(gè)特性

    流(Stream)是Java8為了實(shí)現(xiàn)最佳性能而引入的一個(gè)全新的概念。接下來(lái)通過(guò)本文給大家分享Java8中流的性能,需要的朋友參考下吧
    2017-11-11
  • springboot文件上傳保存路徑的問(wèn)題

    springboot文件上傳保存路徑的問(wèn)題

    這篇文章主要介紹了springboot文件上傳保存路徑的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Spring生命周期回調(diào)與容器擴(kuò)展詳解

    Spring生命周期回調(diào)與容器擴(kuò)展詳解

    這篇文章主要介紹了Spring生命周期回調(diào)與容器擴(kuò)展詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12

最新評(píng)論