Spring Boot 中的 AOP,到底是 JDK 動態代理還是 Cglib 動態代理?
好啦,開始今天的正文。
大家都知道,AOP 底層是動態代理,而 Java 中的動態代理有兩種實現方式:
- 基於 JDK 的動態代理
- 基於 Cglib 的動態代理
這兩者最大的區別在於基於 JDK 的動態代理需要被代理的物件有介面,而基於 Cglib 的動態代理並不需要被代理物件有介面。
那麼小夥伴們不禁要問,Spring 中的 AOP 是怎麼實現的?是基於 JDK 的動態代理還是基於 Cglib 的動態代理?
1. Spring
先來說結論,Spring 中的動態代理,具體用哪種,分情況:
如果代理物件有介面,就用 JDK 動態代理,否則就是 Cglib 動態代理。
如果代理物件沒有介面,那麼就直接是 Cglib 動態代理。
來看看這段來自官方文件的說辭:
可以看到,即使在最新版的 Spring 中,依然是如上策略不變。即能用 JDK 做動態代理就用 JDK,不能用 JDK 做動態代理就用 Cglib,即首選 JDK 做動態代理。
2. Spring Boot
Spring Boot 和 Spring 一脈相承,那麼在動態代理這個問題上是否也是相同的策略呢?抱歉,這個還真不一樣。
Spring Boot 中對這個問題的處理,以 Spring Boot2.0 為節點,前後不一樣。
在 Spring Boot2.0 之前,關於 Aop 的自動化配置程式碼是這樣的(Spring Boot 1.5.22.RELEASE):
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false) public static class CglibAutoProxyConfiguration { } }
可以看到,這個自動化配置主要是在討論 application.properties 配置檔案中的 spring.aop.proxy-target-class 屬性的值。
具體起作用的是 @ConditionalOnProperty 註解,關於這個註解中的幾個屬性,鬆哥也來稍微說下:
- prefix:配置檔案的字首。
- name:配置檔案的名字,和 prefix 共同組成配置的 key。
- having:期待配置的值,如果實際的配置和 having 的值相同,則這個配置就會生效,否則不生效。
- matchIfMissing:如果開發者沒有在 application.properties 中進行配置,那麼這個配置類是否生效。
基於如上介紹,我們很容易看出:
- 如果開發者設定了spring.aop.proxy-target-class 為 false,則使用 JDK 代理。
- 如果開發者設定了spring.aop.proxy-target-class 為 true,則使用 Cglib 代理。
- 如果開發者一開始就沒配置spring.aop.proxy-target-class 屬性,則使用 JDK 代理。
- 這是 Spring Boot 2.0 之前的情況。
再來看看 Spring Boot 2.0(含)之後的情況(Spring Boot 2.0.0.RELEASE):
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }
可以看到,大部分配置都是一樣的,有一個地方不太相同,那就是 matchIfMissing 屬性的值。可以看到,從 Spring Boot2.0 開始,如果使用者什麼都沒有配置,那麼預設情況下使用的是 Cglib 代理。
3. 實踐
最後我們寫一個簡單的例子驗證一下我們的想法。
首先建立一個 Spring Boot 專案(本案例使用最新版 Spring Boot,即預設使用 Cglib 代理),加入三個依賴即可,如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
接下來我們建立一個 IUserService 介面,如下:
public interface IUserService { void hello(); }
然後我們再來建立一個該介面的實現類:
@Service public class UserServiceImpl implements IUserService { @Override public void hello() { } }
方法不用實現。
再來一個簡單的切面:
@EnableAspectJAutoProxy @Aspect @Component public class LogAspect { @Before("execution(* org.javaboy.demo.UserServiceImpl.*(..))") public void before(JoinPoint jp) { System.out.println("jp.getSignature().getName() = " + jp.getSignature().getName()); } }
最後再來一個簡單的測試方法,注入 IUserService 例項:
@RestController public class HelloController { @Autowired IUserService iUserService; @GetMapping("/hello") public void hello() { iUserService.hello(); } }
DBUEG 執行一下,就可以看到 IUserService 是通過 Cglib 來代理的。
如果我們想用 JDK 來代理,那麼只需要在 application.properties 中新增如下配置即可:
spring.aop.proxy-target-class=false
新增完成後,重新 DEBUG,如下圖:
可以看到,已經使用了 JDK 動態代理了。
如果用的是 Spring Boot 1.5.22.RELEASE 這個版本,那麼即使不在 application.properties 中新增配置,預設也是 JDK 代理,這個我就不測試了,小夥伴們可以自己來試試。
4. 小結
總結一下:
- Spring 中的 AOP,有介面就用 JDK 動態代理,沒有介面就用 Cglib 動態代理。
- Spring Boot 中的 AOP,2.0 之前和 Spring 一樣;2.0 之後首選 Cglib 動態代理,如果使用者想要使用 JDK 動態代理,需要自己手動配置。
just this。
- Spring中實現非同步呼叫的方式有哪些?
- 帶引數的全型別 Python 裝飾器
- 整理了幾個Python正則表示式,拿走就能用!
- SOLID:開閉原則Go程式碼實戰
- React中如何引入CSS呢
- 一個新視角:前端框架們都卷錯方向了?
- 編碼中的Adapter,不僅是一種設計模式,更是一種架構理念與解決方案
- 手寫程式語言-遞迴函式是如何實現的?
- 一文搞懂模糊匹配:定義、過程與技術
- 新來個阿里 P7,僅花 2 小時,做出一個多執行緒永動任務,看完直接跪了
- Puzzlescript,一種開發H5益智遊戲的引擎
- @Autowired和@Resource到底什麼區別,你明白了嗎?
- CSS transition 小技巧!如何保留 hover 的狀態?
- React如此受歡迎離不開這4個主要原則
- LeCun再炮轟Marcus: 他是心理學家,不是搞AI的
- Java保證執行緒安全的方式有哪些?
- 19個殺手級 JavaScript 單行程式碼,讓你看起來像專業人士
- Python 的"self"引數是什麼?
- 別整一坨 CSS 程式碼了,試試這幾個實用函式
- 再有人問你什麼是MVCC,就把這篇文章發給他!