对于dubbo框架,对服务引用启动时检查的check配置,官方文档的描述是这样的:
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。
可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
另外,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,否则服务临时不可用时,会抛出异常,拿到 null 引用,如果 check="false",总是会返回引用,当服务恢复时,能自动连上。
关闭某个服务的启动时检查 (没有提供者时报错):
<dubbo:reference interface="com.foo.BarService" check="false" />
关闭所有服务的启动时检查 (没有提供者时报错):
<dubbo:consumer check="false" />
关闭注册中心启动时检查 (注册订阅失败时报错):
<dubbo:registry check="false" />
嗯,总结一下,可以解决循环依赖问题,如果配置为true,则开始加载spring容器时会抛异常出来,如果容器懒加载,则需要关闭check以保证应用启动时对可能被懒加载引用的dubbo服务bean不要被检查。
这些讯息很容易把部分开发者的理解引向:dubbo的check如果为true则永远在容器初始化时检查,而懒加载所加载的bean注入的多是容器启动时还未初始化完成的dubbo服务,所以为了让懒加载的bean在加载时能够注入已经初始化完成的服务,check需设置成false来避开容器初始化时的检查,以防bean还未加载,在应用启动时就先报错了。而如果非懒加载的bean注入了确定在当前应用启动后才会初始化的服务,即便引用此服务时配置了check=false,也仅仅是在应用启动时不会抛出异常,在使用时依然无法连上服务,因为并没有使用懒加载为其延迟出初始化的时间。
这也会产生一个理解误区:想要引用在当前应用之后初始化的服务,则需要使用懒加载策略延迟出服务初始化的时间,而check=false这个配置属性则只是为了配合懒加载避过容器初始化时的检查报错而已。
我们假定一个应用场景,A应用与B应用的beanA与beanB同时注入了serviceC,但serviceC需要等A应用与B应用都启动后才可以去初始化,beanA为lazy-init模式,beanB则是立即加载,那这个时候beanA与beanB该怎么初始化呢?
这个时候一定会引发思考:嘿,这还不简单,设置check为false解决循环依赖,beanA是懒加载的,check为true的话应用A启动容器加载时就会检查,但这个时候serviceC一定是没有初始化好的,如果报错的话,那到了beanA加载的时候,serviceC就注不进去了吧,我把它设置成false,这样等beanA初始化的时候就可以让已经初始化好了的serviceC正常加载了。beanB是立即加载的,如果配置check=false,虽然应用启动不会报错了,但容器上来就注入没初始化好的serviceC岂不是还注不进去?那这个check=false对于beanB来说有什么用呢,check=false只是为了和懒加载一起解决循环依赖存在的东西么?beanB又该如何正常加载服务呢?
起码比较笨的我当时是这样想的。于是我进行了测试:
实际在我测试时,对于懒加载的bean所注入的服务引用,无论check配置值为true或为false,都不会影响spring容器的加载。check配置为false对于懒加载及立即加载的bean并没有差异,都可以在服务初始化完成后继续连上。差异产生于check为true时。
那么检查是在哪里进行的?这步操作实际是根据bean的初始化时机进行的,对于非懒加载的bean,bean初始化的时候等同于容器初始化的时候,如有未初始化完成的服务,check=true则会阻止容器的启动,让应用在启动时就报错,让开发者在应用启动时就知道:我有未初始化好的服务引用正在注入,使用时会出现问题,从而及时加以调整。
对于懒加载的bean而言,bean初始化的时候并非是容器初始化的时候,这种条件下check=true会导致在使用这个bean时才执行检查,如果这个时候bean所注入的服务引用还未初始化完成,则检查机制会抛出异常,并注入null引用,这时容器已经加载完成,应用也已启动,除非应用重启,否则使用时不会再次连上服务,null引用会导致nullpoint异常的产生,为了避免这种情况,对于懒加载的bean,应将check设置为false,并非是要避免应用启动时的异常,而是要避免bean的异常初始化导致的错误bean产生。这是配置check=false对于懒加载bean所注入服务引用的必要性真正所在。
再次总结,根据官方文档的描述,理解不到位的开发者难免会将循环依赖问题与懒加载和check=false的配置连起来思考,这样难免会陷入死结,最后理解为配置check=false来帮助懒加载解决循环依赖的问题。
首先check的配置在立即加载的环境下可以来检查是否有服务未初始化,设置为true,如容器初始化时有未初始化的服务被注入可抛出异常让应用初始化无法完成,设置为false则可以不影响应用的启动,后续服务初始化完成后依然可以正常连上。也就是说check=false的配置本身可以解决循环依赖的问题。
懒加载是一种bean加载策略,并非和dubbo有直接的联系,也并非直接为了解决服务的循环依赖而存在,这种加载方式主要作用在于节约资源,提升性能,亦可以解决循环依赖的问题,只是如有加载了dubbo服务引用的bean要应用懒加载策略,需要check设为false来防止出现未完全装配的bean影响应用的正常运行。