推荐阅读:

阿里工作十年拿下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

整理一下思路

  1. 有人说 Spring5 开始 AOP 默认使用 CGLIB 了
  2. Spring Framework 5.x 文档和 @EnableAspectJAutoProxy源码注释都说了默认是使用 JDK 动态代理
  3. 程序运行结果说明,即使继承了接口,设置proxyTargetClass为false,程序依旧使用 CGLIB 代理

等一下,我们是不是遗漏了什么?

示例程序是使用 SpringBoot 来运行的,那如果不用 SpringBoot,只用 Spring Framework 会怎么样呢?

运行环境:Spring Framework 5.2.0.RELEASE 版本。 UserServiceImpl 和 UserServiceAspect 类和上文一样,这里不在赘述。

运行结果表明: 在 Spring Framework 5.x 版本中,如果类实现了接口,AOP 默认还是使用 JDK 动态代理。

再整理思路

  1. Spring5 AOP 默认依旧使用 JDK 动态代理,官方文档和源码注释没有错。
  2. SpringBoot 2.x 版本中,AOP 默认使用 cglib,且无法通过proxyTargetClass进行修改。
  3. 那是不是 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。

总结

  1. Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
  2. SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB。
  3. 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项spring.aop.proxy-target-class=false来进行修改,proxyTargetClass配置已无效。

转载自作者:程序员小黑
链接:https://juejin.im/post/5db7870a518825647178f16c

jdk源码分析书籍 pdf_什么?Spring5 AOP 默认使用Cglib?从现象到源码深度分析相关推荐

  1. jdk源码分析书籍 pdf_如何阅读源码?

    点击上方"IT牧场",选择"设为星标" 技术干货每日送达! 阅读源码是每个优秀开发工程师的必经之路,那么这篇文章就来讲解下为什么要阅读源码以及如何阅读源码. 首 ...

  2. python数据挖掘与分析实战pdf_《Python数据分析与挖掘实战》PDF+完整源码

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 本书共15章,分两个部分:基础篇.实战篇.基础篇介绍了数据挖掘的基本原理,实战篇介绍了一个个真实案例,通过对案例深入浅出的剖析,使读者在不知不觉中通过案例 ...

  3. 软件工程导论张海蕃书籍pdf_[计算机科学与技术] VB6仓库管理系统本科论文+源码...

    摘 要 本毕业论文系统的描述了毕业设计的全过程,它是一个历时近三个月的毕业设计的总结,也是毕业设计作品的功能说明书.毕业论文首先对毕业设计课题"仓库管理系统"进行系统分析,确定系统 ...

  4. 什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 群聊天 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 ...

  5. spring中aop默认使用jdk动态代理,springboot2以后默认使用cglib来实现动态代理详解

    Spring5 AOP 默认使用 Cglib 了?我第一次听到这个说法是在一个微信群里: 真的假的?查阅文档 刚看到这个说法的时候,我是保持怀疑态度的. 大家都知道 Spring5 之前的版本 AOP ...

  6. get这款工具,不会机器学习也能轻松搞定深度分析

    机器学习是一门多学科交叉专业,涵盖概率论知识.统计学知识.近似理论知识和复杂算法知识.机器学习算法是一类从数据中自动分析获得规律,并利用规律对未知数据进行预测的算法.通过计算机对数据的处理和对算法的运 ...

  7. Spring源码分析(一):从哪里开始看spring源码(系列文章基于Spring5.0)

    一.概述 对于大多数第一次看spring源码的人来说,都会感觉不知从哪开始看起,因为spring项目源码由多个子项目组成,如spring-beans,spring-context,spring-cor ...

  8. 深度分析Java的ClassLoader机制(源码级别)

    转载自 深度分析Java的ClassLoader机制(源码级别) Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取 ...

  9. Toast源码深度分析

    目录介绍 1.最简单的创建方法 1.1 Toast构造方法 1.2 最简单的创建 1.3 简单改造避免重复创建 1.4 为何会出现内存泄漏 1.5 吐司是系统级别的 2.源码分析 2.1 Toast( ...

最新文章

  1. matlab gm 1 1,MATLAB 如何用GM(1,1)做预测?请大神帮忙!
  2. 区块链时代的拜占庭容错:Tendermint(五)
  3. java 删除指定目录_Java 删除目录 指定文件
  4. 23.网市场云建站系统部署
  5. 内核如何为各种系统调用服务
  6. 论文阅读:Semantic Human Matting
  7. Thrift IDL使用方式
  8. VirtualBox一类系统只能出现一个?
  9. 红外测距模块工作原理_红外测温仪方案工作原理
  10. android数据共享 设计,水文数据共享平台移动端的设计与实现
  11. obj类型的3d人体模型解读
  12. 【文献阅读】CCNet: Criss-Cross Attention for Semantic Segmentation
  13. 酷狗音乐解析API,支持搜索、歌单、单曲、专辑、MV解析、多音质切换、图片大小切换
  14. 性能测试流程指南和工具推荐
  15. HBuilder开发旅游类APP(二) ----- mui结合H5+,快速实现首页、登录、注册等功能
  16. 铁路信号基础知识——信号部分
  17. Linux将正在运行的前台程序放到后台执行
  18. python爬虫requests的库使用详解
  19. HowTo如何制作一个文字冒险游戏-里篇(1)
  20. windows系统安装php,运行php

热门文章

  1. OneOfT1,…,Tn清新
  2. 使用 C# 9 的records作为强类型ID - JSON序列化
  3. 被 C# 的 ThreadStatic 标记的静态变量,都存放在哪里了?
  4. ASP.NET Core集成Nacos配置中心之适配多格式配置
  5. 聊聊面试的事(应聘方)
  6. 机器学习 ML.NET 发布 1.0 RC
  7. 10个小技巧助您写出高性能的ASP.NET Core代码
  8. 打造自己的.NET Core项目模板
  9. .Net Core 2.1 通用主机(Core 在控制台应用程序中的应用)
  10. 谈谈surging引擎的tcp、http、ws协议和如何容器化部署