Spirng和SpringBoot中的Aop優先使用的是JDK動態代理還是Cglib
一、Spring 中 Aop 的實現
如果代理物件有介面,就用 JDK 動態代理。JDK 動態代理是利用反射機制生成一個實現代理介面的匿名類,在呼叫具體方法時,會先呼叫實現了 InvokeHandler 介面的 invoke() 方法,來實現業務增強
如果代理物件沒有介面,那麼就直接使用 Cglib 動態代理。Cglib 動態代理是利用 asm 開源包,對代理物件類的 class 檔案載入進來,通過修改其位元組碼生成子類來處理
來自 Spring 官方文件的說辭
可以看到,即使在最新版的 Spring 中,依然是如上策略不變。即能用 JDK 做動態代理就用 JDK,不能用 JDK 做動態代理就用 Cglib,即首選 JDK 做動態代理
Spring 中代理物件產生的原始碼
DefaultAopProxyFactory 類的 createAopProxy() 方法,具體原始碼可以參考
@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { // 獲得被代理物件的型別,以確定代理的方式Class<?> targetClass = config.getTargetClass(); // 如果物件型別是介面,或者是JAVA的動態代理類,那麼就呼叫JDK的動態代理方法生成代理物件 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 否則使用CGLIB生成代理物件 return new ObjenesisCglibAopProxy(config); }else { return new JdkDynamicAopProxy(config); } }
二、SpringBoot 中 Aop 的實現
SpringBoot 和 Spring 一脈相承,那麼在動態代理這個問題上是否也是相同的策略呢?抱歉,這個還真不一樣,SpringBoot 中對這個問題的處理,以 SpringBoot 2.0 為節點,前後不一樣
2.1、SpringBoot 2.0 之前
關於 Aop 的自動化配置程式碼是這樣的(SpringBoot 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/yml 配置檔案中的 spring.aop.proxy-target-class 屬性的值
具體起作用的是 @ConditionalOnProperty 註解,關於這個註解中的幾個屬性
- prefix:配置檔案的字首
- name:配置檔案的名字,和 prefix 共同組成配置的 key
- having:期待配置的值,如果實際的 spring.aop.proxy-target-class 配置和 having 的值相同,則這個配置就會生效,否則不生效
- matchIfMissing:如果開發者沒有在 application.properties/yml 中進行配置,那麼這個配置類是否生效
基於如上介紹,我們很容易看出
- 如果開發者設定了 spring.aop.proxy-target-class 為 false,則使用 JDK 代理
- 如果開發者設定了 spring.aop.proxy-target-class 為 true,則使用 Cglib 代理
- 如果開發者一開始就沒有配置 spring.aop.proxy-target-class 屬性,則使用 JDK 代理
這是 SpringBoot 2.0 之前的情況
2.2、SpringBoot 2.0 之後
再來看看 SpringBoot 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 { } }
AopAutoConfiguration
這個類是幹嘛的?
AopAutoConfiguration 這個類就是一個配置類,SpringBoot 會去掃描這種配置類,並將其載入進 Spring 容器中,但是載入進 Spring 容器中是有條件的,這就使用到了 @Conditional 系列的條件註解
@ConditionalOnClass 註解:當類路徑下存在 EnableAspectJAutoProxy 或 Aspect 或 Advice 或 AnnotatedElement 任意一個類時,Spring 容器才會去載入例項化 AopAutoConfiguration 類
對於 EnableAspectJAutoProxy 這個類它存在於 spring-context 的 jar 包中,即無需引入 aop 的依賴;而對於 Aspect 和 Advice 類就必須引入以下依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
可以看到,大部分配置都是一樣的,有一個地方不太相同,那就是 matchIfMissing 屬性的值。可以看到,從 SpringBoot 2.0 開始,如果使用者什麼都沒有配置,那麼預設情況下使用的是 Cglib 代理
2.3、2.0之後的測試
建立一個 SpringBoot 專案(版本 2.0.9.RELEASE),進行測試
Maven 主要依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency>
下面使用的是 Spring 的事務進行測試,是不需要引入 aop 依賴的,在上面已經闡述了原因,AopAutoConfiguration 這個配置類依然可以被 Spring 載入進容器中
如果使用了 @Aspect 註解進行了切面增強,那麼此時必須引入 aop 的依賴
Service 層
簡單的進行插入資料操作,新增 @Transactional 註解
@Slf4j @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Transactional @Override public int addUser(List<User> list) { list.forEach(item -> { log.info("item的值為:" + item.toString()); }); return userMapper.batchAddUser(list); } }
Controller
層
@Controller public class UserController { @Autowired private UserService userService; @PostMapping(path = "/batchInsertUser") @ResponseBody public int batchInsertUser() { List<User> list = new ArrayList<>(); User user1 = new User("李四", "123456", "李四"); User user2 = new User("張三", "123123", "張三"); list.add(user1); list.add(user2); return userService.addUser(list); } }
Debug
測試
預設配置測試
測試結論:從 SpringBoot 2.0
開始,如果使用者什麼都沒有配置,那麼預設情況下優先使用的是 Cglib
代理
使用 JDK
進行代理
如果想用 JDK
來代理,那麼只需要在 application.properties
中新增如下配置即可
spring.aop.proxy-target-class=false
新增完成後,重新 DEBUG
,如下圖
三、小結
Spring 中的 AOP,有介面就用 JDK 動態代理,沒有介面就用 Cglib 動態代理;並優先使用 JDK 動態代理
SpringBoot 中的 AOP,在 2.0 版本之前和 Spring 一樣
SpringBoot 中的 AOP,在 2.0 版本之後首選 Cglib 動態代理,如果使用者想要使用 JDK 動態代理,需要自己手動配置
摘自:https://blog.csdn.net/weixin_38192427/article/details/121621872