前两天在看 SOFABoot 的时候,看到一个让我眼前一亮的东西,来给大家盘一下。
SOFABoot,你可能不眼熟,但是没关系,本文也不是给你讲这个东西的,你就认为它是 SpringBoot 的变种就行了。
因为有蚂蚁金服背书,所以主要是一些金融类的公司在使用这个框架:
官方介绍是这样的:
SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。
上面这些功能都很强大,但是我主要是分享一下它的这个小功能:
https://help.aliyun.com/document_detail/133162.html
这个功能可以让 Bean 的初始化方法在异步线程里面执行,从而加快 Spring 上下文加载过程,提高应用启动速度。
为什么看到功能的时候,我眼前一亮呢,因为我很久之前写过这篇文章《我是真没想到,这个面试题居然从11年前就开始讨论了,而官方今年才表态。》
里面提到的面试题是这样的:
Spring 在启动期间会做类扫描,以单例模式放入 ioc。但是 spring 只是一个个类进行处理,如果为了加速,我们取消 spring 自带的类扫描功能,用写代码的多线程方式并行进行处理,这种方案可行吗?为什么?
当时通过 issue 找到了官方对于这个问题回复总结起来就是:应该是先找到启动慢的根本原因,而不是把问题甩锅给 Spring。这部分对于 Spring 来说,能不动,就别动。
仅从“启动加速-异步初始化方法”这个标题上来看,Spring 官方不支持的东西 SOFABoot 支持了。所以这玩意让我眼前一亮,我倒要看看你是怎么搞得。
先说结论:SOFABoot 的方案能从一定程度上解决问题,但是它依赖于我们编码的时候指定哪些 Bean 是可以异步初始化的,这样带来的好处是不必考虑循环依赖、依赖注入等等各种复杂的情况了,坏处就是需要程序员自己去识别哪些类是可以异步初始化的。
我倒是觉得,程序员本来就应该具备“识别自己的项目中哪些类是可以异步初始化”的能力。
但是,一旦要求程序员来主动去识别了,就已经“输了”,已经不够惊艳了,在实现难度上就不是一个级别的事情了。人家 Spring 想的可是框架给你全部搞定,顶多给你留一个开关,你开箱即用,啥都不用管。
但是总的来说,作为一次思路演变为源码的学习案例来说,还是很不错的。
我们主要是看实现方案和具体逻辑代码,以 SOFABoot 为抓手,针对其“异步初始化方法”聚焦下钻,把源码当做纽带,协同 Spring,打出一套“我看到了->我会用了->我拿过来->我看懂了->是我的了->写进简历”的组合拳。
先搞个 Demo 出来,演示一波效果,先让你直观的看到这是个啥玩意。
这个 Demo 非常之简单,几行代码就搞定。
先搞两个 java 类,里面有一个 init 方法:
然后把他们作为 Bean 交给 Spring 管理,Demo 就搭建好了:
直接启动项目,启动时间只需要 1.152s,非常丝滑:
然后,注意,我要稍微的变一下形。
在注入 Bean 的时候触发一下初始化方法,模拟实际项目中在 Bean 的初始化阶段,既在 Spring 项目启动过程中,做一些数据准备、配置拉取等相关操作:
再次重启一下项目,因为需要执行两个 Bean 的初始化动作,各需要 5s 时间,而且是串行执行,所以启动时间直接来到了 11.188s:
那么接下来,就是见证奇迹的时刻了。
我加上 @SofaAsyncInit 这样的一个注解:
你先别管这个注解是哪里来的,从这个注解的名称你也知道它是干啥的:异步执行初始化。
这个时候我再启动项目:
从日志中可以看到:
所以 @SofaAsyncInit 这个注解实现了“指定 Bean 的初始化方法实现异步化”。
你想想,如果你有 10 个 Bean,每个 Bean 都需要 1s 的时间做初始化,总计 10s。
但是这些 Bean 之间其实不需要串行初始化,那么用这个注解,并行只需要 1s,搞定。
到这里,你算是看到了这样的东西存在,属于“我看到了”。
接下来,我们进入到“我会用了”这个环节。
在解读原理之前,我还得告诉你这个注解到底是怎么来的。
它属于 SOFABoot 框架里面的注解,首先你得把你的 SpringBoot 修改为 SOFABoot。
这一步参照官方文档中的“快速开始”部分,非常的简单:
https://www.sofastack.tech/projects/sofa-boot/quick-start/
第一步就是把项目中 pom.xml 中的:
org.springframework.boot