务实优化:将本地单元测试Spring启动速度从1分半优化到16秒,全流程详解
-
1
-
背后的故事
某个平凡熟悉的早上,传来测试同学的一阵哀嚎:那个谁!你提测的代码连运行都不能运行,苦涩。
我默不作声,因为主项目还没有完全服务化,主项目的整体
war
包太大,加上从来没有讲究过,开发同学跑一个测试用例,往往启动
Spring
就要花一分半钟,哪里有心情按规范跑单测呢?同问了几个开发同学也都有同样的痛点,感觉解决单测环境刻不容缓,古人云:工欲善其事,必先利其器,对吧?
-
2
-
来了,老弟
将日志级别调至Debug级别
没错,这是我们需要做的第一步最重要的步骤,开启单测,把日志打到一个文件里,从头撸到尾,究竟你这加载的一分半钟究竟干嘛了~
一些Spring配置初始化
根据ComponentScan扫路径的Class类,加入待注入候选列表
根据Mapper扫路径的Class类,加入待注入候选列表
对扫出来的Mapper创建MapperFactoryBean
创建注入@Configuration里@Bean注解的Bean的BeanDefinitions
预加载一些Bean
一些组件例如,PostProcessor、Advisor初始化
一些中间件例如数据库、缓存、消息队列加载
扫描的Bean的初始化,依赖注入。。
Bean的PostConstruct开始跑
....//可能不是很全,列举了其中一部分
默默的看了眼日志,20M,妈耶,引了一个
ApplicationContextAware
看了一下
BeanFactory
,好吧,加载了1500个
Bean
,
Spring
默认的Ioc容器会把所有的Bean在启动时,都加载成功,首先想到的措施是让Bean懒加载,按需加载,不用的就不加载嘛,很简单!
坑来了:如何对Bean进行懒加载?
简单的网上冲浪了一下,我们需要将
注解法:@ComponentScan(value = "com.evanyz",lazyInit = true) //将这个配置设为true
//xml里是beans里有一个default-lazy-init标签
可是设置成功之后,完全不生效,还是1500个,尝试许久,还是没生效,感觉很懵逼,甚至都有点开始怀疑
Spring
了。
排查许久后,突然发现为什么
Debug
的时候,会报一些该Bean重复已存在忽略的错,突然灵光一现。MD,我们项目里写代码根本不讲究,每一个子项目里,例如
common
、
biz
都含有
Spring
的初始化文件。
也就是说,从这个初始化配置之后,继续扫其他的配置文件,还是会继续加载,导致之前的配置失效。。
举例:
我在test的初始化类里配置了 @ComponentScan(value = "com.evanyz",lazyInit = true) ,
当他扫到biz子项目的时候,发现另外一个Spring的配置文件是
@ComponentScan(value = "com.evanyz")
这时候懒加载会被覆盖掉,就不生效了。。
这时候想到的办法就是 在test配置里加上一些操作,解释见注解
@ComponentScan(
value = {com.evanyz"},
//排除一些Bean
excludeFilters = {
//做点小优化,让他把一些在跑单测时的扩展点不要注入
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
SmsServicePostProcessor.class, CatUrlPostProcessor.class,
//按政策排除,把一些其他项目里的Spring配置去掉
@ComponentScan.Filter(type = FilterType.REGEX, pattern = {
"com\\.evanyz\\.test\\..*",
"com\\.evanyz\\.biz\\.springconfig..*",
"com\\.evanyz\\.common\\.springconfig..*",
"com\\.evanyz\\.common\\.cat..*"
//最后清掉了发现,还是有一些配置被加载了
//一不做二不休,我全干掉,搞白名单还不行吗?哭哭