-
问题描述:现有三个配置类,通过
@AutoConfigureBefore
注解保证加载顺序依次为EncryptionPropertyConfig
ApiAutoConfiguration
RedisAutoConfiguration
。 -
实际情况:加载顺序为
ApiAutoConfiguration
RedisAutoConfiguration
EncryptionPropertyConfig
, -
其中
ApiAutoConfiguration
和RedisAutoConfiguration
的加载顺序遵从@AutoConfigureBefore注解控制,可以自由调整顺序,但EncryptionPropertyConfig
不论如何配置,都无法在其他两个配置之前加载。
按照 使用@AutoConfigureBefore调整配置顺序竟没生效 中的描述,自动扫描路径下的配置加载优先级更高,但实测,把EncryptionPropertyConfig
放在自动扫描路径下,依然晚于其他两个配置加载。
自定义配置类,测试加载顺序,任意调控顺序,结果都符合 预期。
所以可以推测,ApiAutoConfiguration
和 RedisAutoConfiguration
有问题,这两个配置类的优先级明显比其他配置类高一档,但两者之间属于同一档。
ApiAutoConfiguration
和 RedisAutoConfiguration
的代码如下所示:
orz,有什么东西导致了这两个配置类的优先级变得更高了吗?
经验证,可以排除以下因素:
-
@Bean
-
EnvironmentAware
所以,初步推测是 org.springframework.boot.context.properties.bind.Binder
的问题,主动绑定属性,导致提升了该配置类的优先级,同样导致 @AutoConfigureBefore
注解失效。
验证如下:
在ConfigTest
中使用Binder
加载配置文件中的信息,同时通过 @AutoConfigureBefore
注解设置ConfigTest2
先于 ConfigTest
加载。
结果,ConfigTest
更先加载。
所以,问题出在 org.springframework.boot.context.properties.bind.Binder
上,只要在EncryptionPropertyConfig
也使用一下org.springframework.boot.context.properties.bind.Binder
,随便读取一个配置,是不是就可以使 @AutoConfigureBefore
生效了?
并不是!!!
被误导了。实测发现,如果随便使用Binder绑定一个属性,并不能导致该配置类地加载优先级提升一档。
所以可以排除:
-
@Bean
-
EnvironmentAware3
-
Binder
进一步排查发现,实际上和手动注入的bean对象有关,ApiAutoConfiguration
和 RedisAutoConfiguration
之所以能够比其他配置类的加载早一档,因为他们内部定义的需要手动注册的bean对象都实现了一个接口:BeanDefinitionRegistryPostProcessor
,如果配置类注册的bean对象实现了这个接口,会导致当前配置类的加载优先级高一个挡位。
假设数值越大优先级越高, AutoConfigureBefore
或者AutoConfigureAfter
注解对配置优先级的影响是1-100,那么配置类中注册实现BeanDefinitionRegistryPostProcessor
接口的bean对象,该配置类的优先级直接+1000,所以相对于没有该类型bean对象的配置类,它们拥有更高的加载优先级,而对于同样拥有该类型bean对象的配置类,都加了1000,相对而言等同于没加,所以AutoConfigureBefore
又开始生效了。
测试验证如下:
首先定义配置类ConfigTest
,并设置@AutoConfigureBefore(RedisAutoConfiguration.class)
:
其中BeanTest
是一个实现了BeanDefinitionRegistryPostProcessor
接口的bean对象:
结果符合预期,ConfigTest
加载顺序先于 RedisAutoConfiguration
但实际上配置类的加载顺序非常容易受到干扰,已知出现以下情况,会出现AutoConfigureBefore
失效的情况:
-
@SpringBootApplication(scanBasePackages = "com.**.*")
包扫描路径下的配置类,加载顺序由spring内部决定,无法自定义。 -
配置类注册的bean对象实现了
BeanDefinitionRegistryPostProcessor
接口,加载顺序直接提升一个档次。 -
如果使用
AutoConfigureBefore
注解定义加载顺序 A > C,B > C,spring内部决定了B>A,所以B先加载,但是B中引用了C中注册的bean对象,所以加载顺序就会变成 B > C > B > A。解释:B最先被构造,但是B中需要用到C里的对象,所以需要加载C,C加载完再加载B剩余的部分,最后加载A。 B和C的引用关系,导致了A>C的AutoConfigureBefore
注解失效。 暨,直接引用的优先级高于AutoConfigureBefore
注解的优先级。 -
etc