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

深度解析Spring AOP @Aspect 原理、實(shí)戰(zhàn)與最佳實(shí)踐教程

 更新時(shí)間:2025年06月23日 17:19:33   作者:hi星塵  
文章系統(tǒng)講解了Spring AOP核心概念、實(shí)現(xiàn)方式及原理,涵蓋橫切關(guān)注點(diǎn)分離、代理機(jī)制(JDK/CGLIB)、切入點(diǎn)類(lèi)型、性能優(yōu)化、常見(jiàn)陷阱及解決方案,并對(duì)比了SpringAOP與AspectJ的編譯時(shí)織入特性,強(qiáng)調(diào)合理應(yīng)用場(chǎng)景與避免濫用的重要性,感興趣的朋友一起看看吧

1. @Aspect 核心概念

1.1 AOP 編程范式

  • 核心思想:將橫切關(guān)注點(diǎn)(如日志、事務(wù)、安全)與業(yè)務(wù)邏輯分離
  • 解決的問(wèn)題:避免代碼中出現(xiàn)大量重復(fù)的"模板代碼"(如每個(gè)Service方法都寫(xiě)事務(wù)控制)

1.2 @Aspect 關(guān)鍵特性

特性說(shuō)明
基于注解比傳統(tǒng)XML配置更簡(jiǎn)潔
代理機(jī)制運(yùn)行時(shí)生成代理對(duì)象(JDK動(dòng)態(tài)代理/CGLIB)
連接點(diǎn)模型支持方法執(zhí)行、異常處理等多種切入點(diǎn)

2. 完整代碼實(shí)現(xiàn)解析

2.1 基礎(chǔ)切面結(jié)構(gòu)

@Aspect
@Component
@Order(1) // 控制多個(gè)切面的執(zhí)行順序
@Slf4j
public class LoggingAspect {
    // 定義可重用的切入點(diǎn)表達(dá)式
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    @Before("serviceLayer()")
    public void logMethodStart(JoinPoint jp) {
        log.info("?? 調(diào)用 {}.{} 參數(shù): {}",
            jp.getTarget().getClass().getSimpleName(),
            jp.getSignature().getName(),
            Arrays.toString(jp.getArgs()));
    }
    @Around("@annotation(com.example.audit.AuditLog)")
    public Object auditLog(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        log.info("?? 審計(jì)日志 - 操作: {}, 耗時(shí): {}ms",
            pjp.getSignature().getName(),
            System.currentTimeMillis() - start);
        return result;
    }
    @AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
    public void logException(JoinPoint jp, Exception ex) {
        log.error("?? 方法 {} 拋出異常: {}", 
            jp.getSignature(), ex.getMessage());
    }
}

2.2 高級(jí)切面示例:接口限流

@Aspect
@Component
public class RateLimitAspect {
    private final RateLimiter limiter = RateLimiter.create(100); // 100 QPS
    @Around("@annotation(rateLimit)")
    public Object limit(ProceedingJoinPoint pjp, RateLimit rateLimit) 
        throws Throwable {
        if (!limiter.tryAcquire(rateLimit.timeout(), rateLimit.timeUnit())) {
            throw new RateLimitException("請(qǐng)求過(guò)于頻繁");
        }
        return pjp.proceed();
    }
}
// 自定義注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimit {
    long timeout() default 1;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

3. 核心原理深度剖析

3.1 代理機(jī)制對(duì)比

代理類(lèi)型條件性能限制
JDK動(dòng)態(tài)代理目標(biāo)實(shí)現(xiàn)接口較高只能代理接口方法
CGLIB代理無(wú)接口類(lèi)略低無(wú)法代理final方法

代理選擇邏輯

// Spring AbstractAutoProxyCreator 的核心邏輯
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    return JdkDynamicAopProxy();
}
return ObjenesisCglibAopProxy();

4. 生產(chǎn)環(huán)境最佳實(shí)踐

4.1 性能優(yōu)化方案

精確切入點(diǎn)匹配

// 不推薦(掃描范圍過(guò)大)
@Pointcut("execution(* *(..))")
// 推薦(限定包路徑+注解)
@Pointcut("execution(* com.yourpackage..service.*.*(..)) && " +
          "@annotation(org.springframework.transaction.annotation.Transactional)")

避免切面內(nèi)部耗時(shí)操作

@Around("serviceLayer()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
    // 錯(cuò)誤示范:在切面內(nèi)進(jìn)行數(shù)據(jù)庫(kù)操作
    // auditRepository.save(...); 
    // 正確做法:只做輕量級(jí)記錄
    long start = System.nanoTime();
    Object result = pjp.proceed();
    long duration = System.nanoTime() - start;
    metrics.record(duration);
    return result;
}

4.2 事務(wù)切面特殊處理

@Aspect
@Component
public class TransactionRetryAspect {
    @Around("@annotation(retry)")
    public Object retry(ProceedingJoinPoint pjp, RetryOnConflict retry) 
        throws Throwable {
        int attempts = 0;
        do {
            try {
                return pjp.proceed();
            } catch (OptimisticLockingFailureException ex) {
                if (++attempts >= retry.maxAttempts()) throw ex;
                Thread.sleep(retry.backoff());
            }
        } while (true);
    }
}

5. 常見(jiàn)陷阱與解決方案

5.1 自調(diào)用問(wèn)題

public class OrderService {
    public void createOrder() {
        this.updateStock(); // 自調(diào)用不走代理!
    }
    @Transactional
    public void updateStock() {...}
}

解決方案

通過(guò)ApplicationContext獲取代理對(duì)象

((OrderService) context.getBean("orderService")).updateStock();

使用AopContext(需開(kāi)啟exposeProxy)

((OrderService) AopContext.currentProxy()).updateStock();

5.2 循環(huán)依賴(lài)問(wèn)題

現(xiàn)象:A切面依賴(lài)B服務(wù),B服務(wù)又需要被A切面代理
解決

@DependsOn("bService") // 強(qiáng)制初始化順序
@Aspect
@Component
public class AAspect {
    @Autowired
    private BService bService;
}

6. 監(jiān)控與調(diào)試技巧

6.1 查看生成的代理類(lèi)

# application.properties
spring.aop.proxy-target-class=true # 強(qiáng)制使用CGLIB
logging.level.org.springframework.aop=DEBUG

6.2 切面執(zhí)行監(jiān)控

@Aspect
@Component
public class AopMonitorAspect {
    @Around("within(@org.aspectj.lang.annotation.Aspect *)")
    public Object monitorAspect(ProceedingJoinPoint pjp) throws Throwable {
        String aspectName = pjp.getTarget().getClass().getSimpleName();
        Monitor.start("aop.aspect." + aspectName);
        try {
            return pjp.proceed();
        } finally {
            Monitor.end();
        }
    }
}

7. 進(jìn)階:編譯時(shí)織入(AspectJ)

7.1 與Spring AOP對(duì)比

特性Spring AOPAspectJ
織入時(shí)機(jī)運(yùn)行時(shí)編譯期/類(lèi)加載期
性能有代理開(kāi)銷(xiāo)無(wú)運(yùn)行時(shí)損耗
能力僅方法級(jí)別支持字段、構(gòu)造器等

7.2 配置示例(Maven插件)

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

總結(jié)

  • 正確使用場(chǎng)景:日志、監(jiān)控、緩存等非核心全局的需求
  • 避免濫用:業(yè)務(wù)規(guī)則校驗(yàn)等核心邏輯應(yīng)放在Service層
  • 性能關(guān)鍵點(diǎn):切入點(diǎn)表達(dá)式精度、切面內(nèi)部復(fù)雜度
  • 推薦組合:Spring AOP + 編譯時(shí)織入(關(guān)鍵路徑)

到此這篇關(guān)于深度解析Spring AOP @Aspect 原理、實(shí)戰(zhàn)與最佳實(shí)踐教程的文章就介紹到這了,更多相關(guān)Spring AOP @Aspect 原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在java代碼中獲取JVM參數(shù)的方法

    在java代碼中獲取JVM參數(shù)的方法

    下面小編就為大家?guī)?lái)一篇在java代碼中獲取JVM參數(shù)的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • javacv-ffmpeg ProcessBuilder批量旋轉(zhuǎn)圖片方式

    javacv-ffmpeg ProcessBuilder批量旋轉(zhuǎn)圖片方式

    為了批量處理大量圖片的旋轉(zhuǎn),可以使用javacv-ffmpeg結(jié)合ProcessBuilder,首先在maven配置文件中添加ffmpeg及javacpp依賴(lài),javacpp支持調(diào)用C/C++方法,而ffmpeg基于C語(yǔ)言,使用ProcessBuilder創(chuàng)建進(jìn)程調(diào)用ffmpeg方法
    2024-09-09
  • Java基礎(chǔ)入門(mén) Swing中間容器的使用

    Java基礎(chǔ)入門(mén) Swing中間容器的使用

    這篇文章主要介紹了Java基礎(chǔ)入門(mén) Swing中間容器的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java使用itext5實(shí)現(xiàn)生成多個(gè)PDF并合并

    Java使用itext5實(shí)現(xiàn)生成多個(gè)PDF并合并

    這篇文章主要為大家詳細(xì)介紹了Java如何使用itext5實(shí)現(xiàn)生成多個(gè)PDF并合并,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-04-04
  • Java多線(xiàn)程死鎖示例

    Java多線(xiàn)程死鎖示例

    這篇文章主要介紹了Java多線(xiàn)程死鎖,結(jié)合實(shí)例形式分析了Java多線(xiàn)程出現(xiàn)死鎖的相關(guān)原因與操作注意事項(xiàng),需要的朋友可以參考下
    2018-08-08
  • java基于QuartzJobBean實(shí)現(xiàn)定時(shí)功能的示例代碼

    java基于QuartzJobBean實(shí)現(xiàn)定時(shí)功能的示例代碼

    QuartzJobBean是Quartz框架中的一個(gè)抽象類(lèi),用于定義和實(shí)現(xiàn)可由Quartz調(diào)度的作業(yè),本文主要介紹了java基于QuartzJobBean實(shí)現(xiàn)定時(shí)功能的示例代碼,具有一定的參考價(jià)值,感興趣可以了解一下
    2023-09-09
  • SpringBoot+BootStrap多文件上傳到本地實(shí)例

    SpringBoot+BootStrap多文件上傳到本地實(shí)例

    這篇文章主要介紹了SpringBoot+BootStrap多文件上傳到本地實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • jquery對(duì)輸入框內(nèi)容的數(shù)字校驗(yàn)代碼實(shí)例

    jquery對(duì)輸入框內(nèi)容的數(shù)字校驗(yàn)代碼實(shí)例

    這篇文章主要介紹了jquery對(duì)輸入框內(nèi)容的數(shù)字校驗(yàn)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Java MyBatis返回兩個(gè)字段作為Map的key和value問(wèn)題

    Java MyBatis返回兩個(gè)字段作為Map的key和value問(wèn)題

    使用MyBatis查詢(xún)兩個(gè)字段并返回Map時(shí),需要注意數(shù)據(jù)量和值的類(lèi)型,直接返回Map會(huì)導(dǎo)致報(bào)錯(cuò),使用@MapKey注解可以生成Map,但值是對(duì)象而不是直接值,為了解決這個(gè)問(wèn)題,可以自定義一個(gè)Map結(jié)果處理器MapResultHandler
    2024-12-12
  • java實(shí)現(xiàn)圖像轉(zhuǎn)碼為字符畫(huà)的方法

    java實(shí)現(xiàn)圖像轉(zhuǎn)碼為字符畫(huà)的方法

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)圖像轉(zhuǎn)碼為字符畫(huà)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03

最新評(píng)論