jdk源码分析书籍 pdf_什么?Spring5 AOP 默认使用Cglib?从现象到源码深度分析
推荐阅读:
阿里工作十年拿下P8,多亏了这些PDF陪我成长(Spring全家桶+源码解析+Redis实战等)zhuanlan.zhihu.com
从入门到熟悉,一步一步带你了解 MySQL 中的「索引」和「锁」zhuanlan.zhihu.com
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里:
真的假的?查阅文档
刚看到这个说法的时候,我是保持怀疑态度的。
大家都知道 Spring5 之前的版本 AOP 在默认情况下是使用 JDK 动态代理的,那是不是 Spring5 版本真的做了修改呢?于是我打开 Spring Framework 5.x 文档,再次确认了一下:
文档地址:http://docs.spring.io/spring/docs…
简单翻译一下。Spring AOP 默认使用 JDK 动态代理,如果对象没有实现接口,则使用 CGLIB 代理。当然,也可以强制使用 CGLIB 代理。
什么?文档写错了?!
当我把官方文档发到群里之后,又收到了这位同学的回复:
SpringBoot 2.x 代码示例
为了证明文档写错了,这位同学还写了一个 DEMO。下面,就由我来重现一下这个 DEMO 程序:
运行环境:SpringBoot 2.2.0.RELEASE 版本,内置 Spring Framework 版本为 5.2.0.RELEASE 版本。同时添加 spring-boot-starter-aop 依赖,自动装配 Spring AOP。
public interface UserService {void work();
}
@Service
public class UserServiceImpl implements UserService {@Overridepublic void work() {System.out.println("开始干活...coding...");}
}
@Component
@Aspect
public class UserServiceAspect {@Before("execution(* com.me.aop.UserService.work(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("UserServiceAspect.....()");}
}
UserServiceImpl实现了UserService接口,同时使用UserServiceAspect对UserService#work方法进行前置增强拦截。
从运行结果来看,这里的确使用了 CGLIB 代理而不是 JDK 动态代理。
难道真的是文档写错了?!
@EnableAspectJAutoProxy 源码注释
在 Spring Framework 中,是使用@EnableAspectJAutoProxy注解来开启 Spring AOP 相关功能的。
Spring Framework 5.2.0.RELEASE 版本@EnableAspectJAutoProxy注解源码如下:
通过源码注释我们可以了解到:在 Spring Framework 5.2.0.RELEASE 版本中,proxyTargetClass的默认取值依旧是false,默认还是使用 JDK 动态代理。
难道文档和源码注释都写错了?!
@EnableAspectJAutoProxy 的 proxyTargetClass 无效了?
接下来,我尝试使用@EnableAspectJAutoProxy来强制使用 JDK 动态代理。
运行环境:SpringBoot 2.2.0.RELEASE 版本,内置 Spring Framework 版本为 5.2.0.RELEASE 版本。
通过运行发现,还是使用了 CGLIB 代理。难道@EnableAspectJAutoProxy 的 proxyTargetClass设置无效了?
Spring Framework 5.x
整理一下思路
- 有人说 Spring5 开始 AOP 默认使用 CGLIB 了
- Spring Framework 5.x 文档和 @EnableAspectJAutoProxy源码注释都说了默认是使用 JDK 动态代理
- 程序运行结果说明,即使继承了接口,设置proxyTargetClass为false,程序依旧使用 CGLIB 代理
等一下,我们是不是遗漏了什么?
示例程序是使用 SpringBoot 来运行的,那如果不用 SpringBoot,只用 Spring Framework 会怎么样呢?
运行环境:Spring Framework 5.2.0.RELEASE 版本。 UserServiceImpl 和 UserServiceAspect 类和上文一样,这里不在赘述。
运行结果表明: 在 Spring Framework 5.x 版本中,如果类实现了接口,AOP 默认还是使用 JDK 动态代理。
再整理思路
- Spring5 AOP 默认依旧使用 JDK 动态代理,官方文档和源码注释没有错。
- SpringBoot 2.x 版本中,AOP 默认使用 cglib,且无法通过proxyTargetClass进行修改。
- 那是不是 SpringBoot 2.x 版本做了一些改动呢?
再探 SpringBoot 2.x
结果上面的分析,很有可能是 SpringBoot2.x 版本中,修改了 Spring AOP 的相关配置。那就来一波源码分析,看一下内部到底做了什么。
源码分析
源码分析,找对入口很重要。那这次的入口在哪里呢?
@SpringBootApplication是一个组合注解,该注解中使用@EnableAutoConfiguration实现了大量的自动装配。
EnableAutoConfiguration也是一个组合注解,在该注解上被标志了@Import。关于@Import注解的详细用法,可以参看笔者之前的文章:http://mp.weixin.qq.com/s/7arh4sVH1…
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
AutoConfigurationImportSelector实现了DeferredImportSelector接口。
在 Spring Framework 4.x 版本中,这是一个空接口,它仅仅是继承了ImportSelector接口而已。而在 5.x 版本中拓展了DeferredImportSelector接口,增加了一个getImportGroup方法:
在这个方法中返回了AutoConfigurationGroup类。这是AutoConfigurationImportSelector中的一个内部类,他实现了DeferredImportSelector.Group接口。
在 SpringBoot 2.x 版本中,就是通过AutoConfigurationImportSelector.AutoConfigurationGroup#process方法来导入自动配置类的。
通过断点调试可以看到,和 AOP 相关的自动配置是通过org.springframework.boot.autoconfigure.aop.AopAutoConfiguration来进行配置的。
真相大白
看到这里,可以说是真相大白了。在 SpringBoot2.x 版本中,通过AopAutoConfiguration来自动装配 AOP。
默认情况下,是肯定没有spring.aop.proxy-target-class这个配置项的。而此时,在 SpringBoot 2.x 版本中会默认使用 Cglib 来实现。
SpringBoot 2.x 中如何修改 AOP 实现
通过源码我们也就可以知道,在 SpringBoot 2.x 中如果需要修改 AOP 的实现,需要通过spring.aop.proxy-target-class这个配置项来修改。
#在application.properties文件中通过spring.aop.proxy-target-class来配置
spring.aop.proxy-target-class=false
这里也提一下spring-configuration-metadata.json文件的作用:在使用application.properties或application.yml文件时,IDEA 就是通过读取这些文件信息来提供代码提示的,SpringBoot 框架自己是不会来读取这个配置文件的。
SringBoot 1.5.x 又是怎么样的
可以看到,在 SpringBoot 1.5.x 版本中,默认还是使用 JDK 动态代理的。
SpringBoot 2.x 为何默认使用 Cglib
SpringBoot 2.x 版本为什么要默认使用 Cglib 来实现 AOP 呢?这么做的好处又是什么呢?笔者从网上找到了一些资料,先来看一个 issue。
Spring Boot issue #5423
Use @EnableTransactionManagement(proxyTargetClass = true) #http://5423github.com/spring-proj…
在这个 issue 中,抛出了这样一个问题:
翻译一下:我们应该使用@EnableTransactionManagement(proxyTargetClass = true)来防止人们不使用接口时出现讨厌的代理问题。
这个"不使用接口时出现讨厌的代理问题"是什么呢?思考一分钟。
讨厌的代理问题
假设,我们有一个UserServiceImpl和UserService类,此时需要在UserContoller中使用UserService。在 Spring 中通常都习惯这样写代码:
@Autowired
UserService userService;
在这种情况下,无论是使用 JDK 动态代理,还是 CGLIB 都不会出现问题。
但是,如果你的代码是这样的呢:
@Autowired
UserServiceImpl userService;
这个时候,如果我们是使用 JDK 动态代理,那在启动时就会报错:
因为 JDK 动态代理是基于接口的,代理生成的对象只能赋值给接口变量。
而 CGLIB 就不存在这个问题。因为 CGLIB 是通过生成子类来实现的,代理对象无论是赋值给接口还是实现类这两者都是代理对象的父类。
SpringBoot 正是出于这种考虑,于是在 2.x 版本中,将 AOP 默认实现改为了 CGLIB。
更多的细节信息,读者可以自己查阅上述 issue。
总结
- Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
- SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB。
- 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,proxyTargetClass配置已无效。
转载自作者:程序员小黑
链接:https://juejin.im/post/5db7870a518825647178f16c
jdk源码分析书籍 pdf_什么?Spring5 AOP 默认使用Cglib?从现象到源码深度分析相关推荐
- jdk源码分析书籍 pdf_如何阅读源码?
点击上方"IT牧场",选择"设为星标" 技术干货每日送达! 阅读源码是每个优秀开发工程师的必经之路,那么这篇文章就来讲解下为什么要阅读源码以及如何阅读源码. 首 ...
- python数据挖掘与分析实战pdf_《Python数据分析与挖掘实战》PDF+完整源码
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 本书共15章,分两个部分:基础篇.实战篇.基础篇介绍了数据挖掘的基本原理,实战篇介绍了一个个真实案例,通过对案例深入浅出的剖析,使读者在不知不觉中通过案例 ...
- 软件工程导论张海蕃书籍pdf_[计算机科学与技术] VB6仓库管理系统本科论文+源码...
摘 要 本毕业论文系统的描述了毕业设计的全过程,它是一个历时近三个月的毕业设计的总结,也是毕业设计作品的功能说明书.毕业论文首先对毕业设计课题"仓库管理系统"进行系统分析,确定系统 ...
- 什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 群聊天 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 ...
- spring中aop默认使用jdk动态代理,springboot2以后默认使用cglib来实现动态代理详解
Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...
- get这款工具,不会机器学习也能轻松搞定深度分析
机器学习是一门多学科交叉专业,涵盖概率论知识.统计学知识.近似理论知识和复杂算法知识.机器学习算法是一类从数据中自动分析获得规律,并利用规律对未知数据进行预测的算法.通过计算机对数据的处理和对算法的运 ...
- Spring源码分析(一):从哪里开始看spring源码(系列文章基于Spring5.0)
一.概述 对于大多数第一次看spring源码的人来说,都会感觉不知从哪开始看起,因为spring项目源码由多个子项目组成,如spring-beans,spring-context,spring-cor ...
- 深度分析Java的ClassLoader机制(源码级别)
转载自 深度分析Java的ClassLoader机制(源码级别) Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取 ...
- Toast源码深度分析
目录介绍 1.最简单的创建方法 1.1 Toast构造方法 1.2 最简单的创建 1.3 简单改造避免重复创建 1.4 为何会出现内存泄漏 1.5 吐司是系统级别的 2.源码分析 2.1 Toast( ...
最新文章
- matlab gm 1 1,MATLAB 如何用GM(1,1)做预测?请大神帮忙!
- 区块链时代的拜占庭容错:Tendermint(五)
- java 删除指定目录_Java 删除目录 指定文件
- 23.网市场云建站系统部署
- 内核如何为各种系统调用服务
- 论文阅读:Semantic Human Matting
- Thrift IDL使用方式
- VirtualBox一类系统只能出现一个?
- 红外测距模块工作原理_红外测温仪方案工作原理
- android数据共享 设计,水文数据共享平台移动端的设计与实现
- obj类型的3d人体模型解读
- 【文献阅读】CCNet: Criss-Cross Attention for Semantic Segmentation
- 酷狗音乐解析API,支持搜索、歌单、单曲、专辑、MV解析、多音质切换、图片大小切换
- 性能测试流程指南和工具推荐
- HBuilder开发旅游类APP(二) ----- mui结合H5+,快速实现首页、登录、注册等功能
- 铁路信号基础知识——信号部分
- Linux将正在运行的前台程序放到后台执行
- python爬虫requests的库使用详解
- HowTo如何制作一个文字冒险游戏-里篇(1)
- windows系统安装php,运行php
热门文章
- OneOfT1,…,Tn清新
- 使用 C# 9 的records作为强类型ID - JSON序列化
- 被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?
- ASP.NET Core集成Nacos配置中心之适配多格式配置
- 聊聊面试的事(应聘方)
- 机器学习 ML.NET 发布 1.0 RC
- 10个小技巧助您写出高性能的ASP.NET Core代码
- 打造自己的.NET Core项目模板
- .Net Core 2.1 通用主机(Core 在控制台应用程序中的应用)
- 谈谈surging引擎的tcp、http、ws协议和如何容器化部署