项目一览
这个 demo 分为两个模块 :
👉 插件模块springboot-aop-plugin
👉 业务模块springboot-aop-plugin-used
模块功能介绍
模块功能介绍
👉 插件模块springboot-aop-plugin 里面提供两个插件
- 插件 A MethodCountingTimesPlugin : 统计方法调用次数
- 插件 B MethodSpendTimePlugin : 计算调用方法所花费的时间
👉 业务模块springboot-aop-plugin-used
- 提供业务 API
- 插件配置类,主要负责 解析
- 插件工厂,主要负责 加载,激活和停用插件
模块功能介绍
使用
将 插件模块 打包成一个 jar 包,然后在 业务模块 中配置好 plugins.json 的 jar 包地址,随后 激活/停用插件,就可以在控制台看到不同的输出效果啦😄
原理图 👇
主要知识点
- 类加载器
- Spring AOP 编程式
效果演示
API 如下🐖
激活插件1
调用方法时会统计该方法的调用次数
关闭插件1
再次激活插件1
顺便激活插件2 效果
还挺好玩的 哈哈 其他就等小伙伴们自己优化了 🐷
主要源码说明
MethodCountingTimesPlugin 插件通过实现这个 MethodBeforeAdvice 来达到 @Before 注解的效果
MethodSpendTimePlugin 插件通过实现这个 MethodInterceptor 来达到 @Around 注解的效果
这部分的知识点可以看上篇文章 👉《Spring AOP内功修炼!!》
代码也很简单,就不多介绍啦👇
PluginConfig
这个配置类呢,就是在初始化时去加载,解析这个配置文件 plugins.json,然后放到这个 map 中
DefaultPluginFactory
激活插件方法如下 👇
也就是通过这个 编程式AOP 来实现
完整项目在 Github 上,链接在文末自取就可以啦~
接下来说说搭建这个小demo 遇到的坑🕳
坑🕳
- 打包插件模块,这里我们用到的是 spring-boot-maven-plugin 插件,打包时会去查找有 main 方法的类,并修改 jar 包结构为 BOOT-INF/classes/ ,这样打出来的包,会导致加载插件时无法解析出增强类,一直都是 ClassNotFoundException
- ClassLoader 的不同,本次 demo 使用的是 JDK11,而在 JDK11 中,AppclassLoader 无法再转换为 URLClassLoader ,区别如下👇
JDK11
JDK8
所以在 JDK11 中无法通过将 AppclassLoader 转换成 URLClassLoader 去判断有没有加载过某个 jar 包
问题思考
完成这个 demo 后,4ye 对 AOP 又有了以下的这些思考~
一. AOP 发生的条件
我们都知道 AOP 是 面向切面编程 ,所以我们得告诉它往哪里切,才有机会创建这个 代理对象 出来~
比如 Spring 提供的这几个注解
- 事务 @Transactional
- 异步 @Async
- 缓存 @Cacheable , @CacheEvict , @CachePut , @Caching 等
这些在 spring-aspects 模块中
关于 Spring 的模块可以参考这篇文章 👉 《Spring的这七大模块你了解吗?》
同时,创建代理对象时,CGLIB 只能代理 非final 类中的 非final,非static 方法。
二. 为啥采用编程式的AOP
这就突出它的优点啦!毕竟编程式才是最灵活的 哈哈。就像 编程式事务 一样,你可以控制事务的粒度,在编程式 AOP 中,你可以控制 Advice 的启动,停止。
三. 优化地方
- MethodCountingTimesPlugin 中是通过 map 来存放不同方法的调用次数的,这个 key 需要考虑怎么和方法挂钩起来,并且唯一 (待优化)
1.实现配置文件的热更新,以及刷新缓存的 Advice (待优化)
新发现
我们这篇的主题是插件,插件可插拔的特点十分方便,同时,我们利用 ClassLoader 实现了 热加载!
但是呢,我了解到它不仅仅有这个功能,它还可以实现对 class 文件的加解密,同时 4ye 也是间接了解到这个 阿里的 pandora 以及解锁了新的源码篇章 spring boot devtools ,很有意思的,争取早点分享出来 嘿嘿 😋