最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

效果

切入com.anfioo下的所有controller層

切入com.anfioo下的所有service層

切入自定義注解@LogRecord

全都開啟

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

一、引入依賴

首先,確保你的Spring Boot項目已經(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屬性(可選)

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

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

并通過@ConfigurationProperties將其綁定到Java類:

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

三、定義切點(Pointcut)

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

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

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

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

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;

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

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

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

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

        // 提取請求的 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 可以改成任意長度
                .mapToObj(i -> "#")
                .collect(Collectors.joining());
        System.out.println(red + line);


        // 記錄請求信息
        logger.info("\n====== ?? 請求信息 ======\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é)束時間,并計算耗時
        long end = System.currentTimeMillis();
        // 記錄響應(yīng)信息
        logger.info("\n====== ?? ? 響應(yīng)信息 ======\n" +
                        "?? Handler    : {}\n" +
                        "?? Response   : {}\n" +
                        "?? 耗時        : {} ms\n" +
                        "==========================",
                classMethod, result, end - start);

        return result;
    }
}

說明

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

五、自定義注解與切面

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

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

并實現(xiàn)對應(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;

    /**
     * 切點:攔截所有帶有 @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(); // 不開啟則跳過
        }

        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" +
                        "?? 耗時        : {} ms\n" +
                        "=====================================",
                methodName, result, end - start);

        return result;
    }
}

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

同理,可以為Service層實現(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())) {
            // 不開啟,直接執(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" +
                    "?? 耗時        : {} ms\n" +
                    "===============================",
                methodName, result, end - start);

        return result;
    }
}

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

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

八、常見問題

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

AOP 的主要好處

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

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

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

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

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

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

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

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

靈活可配置

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

提高開發(fā)效率

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

總結(jié)

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

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

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

相關(guān)文章

  • java編程幾行代碼實現(xiàn)買菜自由

    java編程幾行代碼實現(xiàn)買菜自由

    這篇文章主要為大家介紹了java編程幾行代碼實現(xiàn)買菜自由,需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • SpringMVC中的HandlerAdapter解析

    SpringMVC中的HandlerAdapter解析

    這篇文章主要介紹了SpringMVC中的HandlerAdapter解析,HandlerAdapter是一個關(guān)鍵的組件,用于將請求與處理程序方法進(jìn)行適配和調(diào)度,它充當(dāng)了控制器和處理程序之間的橋梁,負(fù)責(zé)將請求的參數(shù)和處理程序方法進(jìn)行匹配,并將結(jié)果返回給前端,需要的朋友可以參考下
    2023-10-10
  • JAVA中字符串如何與整型數(shù)字相加

    JAVA中字符串如何與整型數(shù)字相加

    這篇文章主要介紹了JAVA中字符串如何與整型數(shù)字相加,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-07-07
  • mapstruct的用法之qualifiedByName示例詳解

    mapstruct的用法之qualifiedByName示例詳解

    qualifiedByName的意思就是使用這個Mapper接口中的指定的默認(rèn)方法去處理這個屬性的轉(zhuǎn)換,而不是簡單的get?set,今天通過本文給大家介紹下mapstruct的用法之qualifiedByName示例詳解,感興趣的朋友一起看看吧
    2022-04-04
  • SpringBoot3使用Swagger3的示例詳解

    SpringBoot3使用Swagger3的示例詳解

    本文介紹了如何在Spring Boot 3項目中使用Swagger3進(jìn)行后端接口的前端展示,首先,通過添加依賴并配置application.yml文件來快速啟動Swagger,然后,詳細(xì)介紹了Swagger3的新注解與Swagger2的區(qū)別,并提供了一些常用注解的使用示例,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Java繼承的實現(xiàn)與繼承限制分析

    Java繼承的實現(xiàn)與繼承限制分析

    這篇文章主要介紹了Java繼承的實現(xiàn)與繼承限制,結(jié)合具體實例形式分析了Java繼承的定義、實現(xiàn)以及繼承的相關(guān)限制,需要的朋友可以參考下
    2019-01-01
  • SpringBoot+Redis防止惡意刷新與暴力請求接口的實現(xiàn)

    SpringBoot+Redis防止惡意刷新與暴力請求接口的實現(xiàn)

    這篇文章主要為大家介紹了如何利用springboot和Redis來實現(xiàn)防止惡意刷新與暴力請求接口,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-06-06
  • SpringMVC+MyBatis聲明式事務(wù)管理

    SpringMVC+MyBatis聲明式事務(wù)管理

    在最近的一個項目中,采用springMVC、mybatis,MySQL、tomcat,事務(wù)管理對于企業(yè)應(yīng)用來說是至關(guān)重要的,即使出現(xiàn)異常情況,它也可以保證數(shù)據(jù)的一致性。Spring Framework對事務(wù)管理提供了一致的抽象,
    2015-08-08
  • vue+springboot上傳文件、圖片、視頻及回顯到前端詳解

    vue+springboot上傳文件、圖片、視頻及回顯到前端詳解

    一般來說vue可以使用axios或者fetch等ajax庫發(fā)送文件請求,而springboot則可以使用Spring MVC的方式來處理上傳文件請求,下面這篇文章主要給大家介紹了關(guān)于vue+springboot上傳文件、圖片、視頻及回顯到前端的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • 學(xué)習(xí)Java之Java中的異常處理機(jī)制詳解

    學(xué)習(xí)Java之Java中的異常處理機(jī)制詳解

    在本文中,小編將帶領(lǐng)大家來學(xué)習(xí)Java的異常處理機(jī)制,包括異常機(jī)制、異常類型、如何捕獲異常、如何拋出異常以及如何創(chuàng)建自定義異常等核心內(nèi)容,感興趣的同學(xué)跟著小編一起來看看吧
    2023-08-08

最新評論

浏阳市| 增城市| 松江区| 东阿县| 马公市| 伊金霍洛旗| 梅河口市| 西盟| 乐清市| 鹤峰县| 禹城市| 垦利县| 万安县| 蕉岭县| 贵州省| 大英县| 琼海市| 全南县| 汾阳市| 青海省| 酒泉市| 门头沟区| 鄂托克前旗| 临武县| 定州市| 滦南县| 思茅市| 都昌县| 永川市| 乌拉特后旗| 宣威市| 淳安县| 宝丰县| 绥江县| 响水县| 柳州市| 洛浦县| 离岛区| 沙田区| 平定县| 定州市|