偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

寫給新手!Spring AOP代理機(jī)制,必須清楚,否則各種失效

開發(fā) 前端
如果要被代理的目標(biāo)對(duì)象實(shí)現(xiàn)了至少一個(gè)接口,則使用 JDK 動(dòng)態(tài)代理,并且目標(biāo)類型實(shí)現(xiàn)的所有接口都將被代理。如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口,則創(chuàng)建一個(gè) CGLIB 代理,這是一個(gè)運(yùn)行時(shí)生成的目標(biāo)類型的子類。

1. 代理機(jī)制

Spring AOP 使用 JDK 動(dòng)態(tài)代理或 CGLIB 來為給定的目標(biāo)對(duì)象創(chuàng)建代理。JDK 動(dòng)態(tài)代理是內(nèi)置在 JDK 中的,而 CGLIB 是一個(gè)常見的開源類定義庫(已重新打包到 spring-core 中)。

如果要被代理的目標(biāo)對(duì)象實(shí)現(xiàn)了至少一個(gè)接口,則使用 JDK 動(dòng)態(tài)代理,并且目標(biāo)類型實(shí)現(xiàn)的所有接口都將被代理。如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)任何接口,則創(chuàng)建一個(gè) CGLIB 代理,這是一個(gè)運(yùn)行時(shí)生成的目標(biāo)類型的子類。

如果你希望強(qiáng)制使用 CGLIB 代理(例如,為了代理目標(biāo)對(duì)象定義的所有方法,而不僅僅是其接口實(shí)現(xiàn)的方法),你可以這樣做。但是,你必須考慮以下問題:

  • final修飾的類不能代理,因?yàn)樗鼈儾荒芾^承。
  • final修飾的方法不能被增強(qiáng),因?yàn)樗鼈儾荒鼙恢貙憽?/li>
  • 私有方法(private methods)無法被增強(qiáng),因?yàn)樗鼈儾荒鼙恢貙憽?/li>
  • 不可見的方法(例如,來自不同包的父類中的包私有方法)無法被增強(qiáng),因?yàn)樗鼈儗?shí)際上是私有的。
  • 你的代理對(duì)象的構(gòu)造函數(shù)不會(huì)被調(diào)用兩次,因?yàn)?CGLIB 代理實(shí)例是通過 Objenesis(它能繞過構(gòu)造函數(shù)) 創(chuàng)建的。然而,如果你的 JVM 不允許繞過構(gòu)造函數(shù),你可能會(huì)看到兩次調(diào)用以及來自 Spring AOP 支持的相應(yīng)調(diào)試日志。
  • 在使用 CGLIB 代理時(shí),你可能會(huì)遇到 Java 模塊系統(tǒng)的限制。作為一個(gè)典型的例子,當(dāng)在模塊路徑上部署時(shí),你不能為 java.lang 包中的類創(chuàng)建 CGLIB 代理。這樣的情況需要一個(gè) JVM 啟動(dòng)標(biāo)志 --add-opens=java.base/java.lang=ALL-UNNAMED,但該標(biāo)志對(duì)模塊不可用。 

要強(qiáng)制使用 CGLIB 代理,請(qǐng)將 <aop:config> 元素的 proxy-target-class 屬性值設(shè)置為 true,如下所示:

<aop:config proxy-target-class="true">
</aop:config>

如果是基于Spring Boot環(huán)境,則通過如下配置:

spring:
  aop:
    proxy-target-class: true

該屬性默認(rèn)值即為true。你還可以使用注解的方式,如下所示:

@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {}

如果同時(shí)配置了,那么自定義注解優(yōu)先于配置文件。

要在使用 @AspectJ 自動(dòng)代理支持時(shí)強(qiáng)制使用 CGLIB 代理,可以將 <aop:aspectj-autoproxy> 元素的 proxy-target-class 屬性值設(shè)置為 true,如下所示:

<aop:aspectj-autoproxy proxy-target-class="true"/>

以上關(guān)于代理機(jī)制及相關(guān)配置的說明。

2. 理解AOP代理

Spring AOP 是基于代理的。在你編寫自己的切面或使用 Spring 框架提供的任何基于 Spring AOP 的切面之前,非常重要的一點(diǎn)是你要理解這句話的實(shí)際含義。

首先,請(qǐng)看下面的代碼片段,它顯示了一個(gè)普通的、未代理的對(duì)象引用:

public class SimplePojo implements Pojo {
  public void foo() {
    this.bar();
  }


  public void bar() {
  }
}

如果你調(diào)用一個(gè)對(duì)象引用上的方法,該方法將直接在該對(duì)象引用上被調(diào)用,如下圖和列表所示:

圖片圖片

public class Main {


  public static void main(String[] args) {
    Pojo pojo = new SimplePojo();
    pojo.foo();
  }
}

當(dāng)客戶端代碼的引用是代理時(shí),情況會(huì)略有不同。請(qǐng)看下圖和代碼片段:

圖片圖片

public class Main {


  public static void main(String[] args) {
    ProxyFactory factory = new ProxyFactory(new SimplePojo());
    factory.addInterface(Pojo.class);
    factory.addAdvice(new RetryAdvice());


    Pojo pojo = (Pojo) factory.getProxy();
    // 通過創(chuàng)建的代理對(duì)象調(diào)用
    pojo.foo();
  }
}

這里的關(guān)鍵是要理解,main(..) 方法中的客戶端代碼持有一個(gè)代理的引用。這意味著對(duì)該對(duì)象引用的方法調(diào)用實(shí)際上是調(diào)用代理的方法。因此,代理可以委托給與該特定方法調(diào)用相關(guān)的所有攔截器(通知)。然而,一旦調(diào)用到達(dá)目標(biāo)對(duì)象(在這個(gè)例子中是 SimplePojo ),目標(biāo)對(duì)象上可能進(jìn)行的任何方法調(diào)用,如 this.bar() 或 this.foo(),都將針對(duì) this 引用進(jìn)行調(diào)用,而不是代理。這一點(diǎn)非常重要。這意味著自我調(diào)用不會(huì)導(dǎo)致與方法調(diào)用相關(guān)聯(lián)的通知有機(jī)會(huì)運(yùn)行。換句話說,通過顯式或隱式的 this 引用進(jìn)行的自我調(diào)用將繞過通知。

要解決這個(gè)問題,可以通過以下方式:

  • 避免自我調(diào)用最好的方法(這里“最好”一詞用得比較寬松)是重構(gòu)你的代碼,以避免自我調(diào)用的發(fā)生。這確實(shí)需要你做一些工作,但這是最好且侵入性最小的方法。
  • 注入自己另一種方法是使用自我注入(self-injection),并通過自我引用而不是 this 來調(diào)用代理上的方法。
  • 使用AopContext.currentProxy()最后這種方法極不推薦。然而,作為最后的手段,你可以選擇將類內(nèi)的邏輯綁定到 Spring AOP,如下例所示:
public class SimplePojo implements Pojo {


  public void foo() {
    ((Pojo) AopContext.currentProxy()).bar() ;
  }
  public void bar() {
  }
}

使用 AopContext.currentProxy() 完全將你的代碼耦合到 Spring AOP,并使類本身意識(shí)到它正在一個(gè) AOP 上下文中使用,這減少了 AOP 的一些好處。同時(shí)你還需要進(jìn)行如下的配置才能使用如上方法:

public class Main {


  public static void main(String[] args) {
    ProxyFactory factory = new ProxyFactory(new SimplePojo());
    factory.addInterface(Pojo.class);
    factory.addAdvice(new RetryAdvice());
    // 必須設(shè)置為true,你才能調(diào)用上面的方法
    factory.setExposeProxy(true);


    Pojo pojo = (Pojo) factory.getProxy() ;
    pojo.foo();
  }
}

如果你是Spring Boot環(huán)境,那么你可以通過下面的方式進(jìn)行設(shè)置:

@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {}

以上內(nèi)容,對(duì)于我們學(xué)習(xí)使用AOP新手來說必須要知道及掌握的內(nèi)容,這樣可以盡可能的減少錯(cuò)誤的發(fā)生。

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2012-05-01 21:27:20

iOS

2024-01-29 08:28:01

Spring事務(wù)失效

2021-03-04 08:06:13

Java代理機(jī)制

2022-09-01 10:40:29

SpringAOPJDK

2024-04-01 08:38:57

Spring@AspectAOP

2018-08-03 12:52:51

首頁彈窗iOS

2018-08-08 20:35:58

APP結(jié)構(gòu)指南導(dǎo)航

2023-10-08 10:14:12

2023-08-02 10:48:47

SpringBean反射

2009-06-19 13:28:30

Spring AOPSpring 2.0

2022-06-07 07:58:45

SpringSpring AOP

2011-06-13 17:43:37

SEO建站

2025-02-26 13:13:52

2018-09-03 12:39:41

信息架構(gòu)設(shè)計(jì)產(chǎn)品

2013-05-22 15:31:07

AOP的CGlib實(shí)現(xiàn)

2009-06-22 10:41:34

Spring.AOP

2022-02-17 13:39:09

AOP接口方式

2015-08-14 13:51:22

程序員

2019-05-10 10:50:04

Spring AOPJDK動(dòng)態(tài)代理CGLIB動(dòng)態(tài)代理

2019-06-18 15:57:25

HTTP緩存機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)