循环依赖
1.1,在拦截器配置类,通过构造器方式,依赖拦截器,重写addInterceptors函数把拦截器注入到拦截器链中。
1.2,而拦截器中也是通过构造器方式,依赖一个thirdAuthClient
1.3,thirdAuthClient(一个feign客户端接口)
项目启动报错,显示这三个类循环依赖
看报错,前两个依赖能理解,但是ThirdAuthClient依赖ThirdInterceptorConfig无法理解,这只是一个@FeignClient,应该是在spring启动之后加载。看类图关系也看不出结果,初步怀疑是跟spring bean加载顺序有关系,虽然看到的是循环依赖,但实际可能是用错了加载方式。
首先关注Spring MVC Interceptor的注入方式
这是官方文档中拦截器的注册方式,直接new出来的。那么关注点就变成了:
为什么Interceptor不可以通过@Autowired方式或者构造器进行注入?
搜了一下,看到这样的说法:
1,SpringBoot项目的Bean装配默认规则是根据Application类所在的包( com.boot.app )位置从上往下扫描
2, 拦截器执行在自动bean初始化之前
这个说法和假说一致,继续思考:拦截器是否确实执行在自动bean初始化之前
在我的使用场景里,我觉得正确的顺序应该是
拦截器的所有依赖(比如ThirdAuthClient)被初始化bean并注入Spring容器
执行拦截器注入
初始化拦截器bean对象,此时需要1中已经存在的bean对象作为拦截器的依赖项
拦截器注入成功
如果这样想,那么说法2和这个想法不一致,或者说,官方文档中定义的拦截器是常规自定义,不需要依赖,直接new就行了,而我自定义的拦截器 通过构造器,显示地声明了依赖 ,可能是这种区别导致了循环依赖。
又找了一下构造器的其他注入方式,如下
可以通过@Bean注入的方式,那么造成拦截器循环依赖的ThirdAuthClient通过@Autowired注入,其他类通过构造器注入
这样在执行我想法中的顺序2的时候,顺序1所需要的bean就准备好了。
首先根据Spring加载及实例化Bean的顺序,如下
循环引用报错应该是Spring在递归解析的时候检测出来的,并且跟我写的构造器方式初始化bean有关系。在网上看到的拦截器的注入方式基本都是手动创建的。
那么这里明确的疑问是:
1,拦截器是否只能手动创建(new出来)?不可以让Spring去管理对象并创建吗?
分析:这个加载过程涉及到了 @ConfigurationProperties
, @Component
, @FeignClient
, @Configuration
, @Component
这些注解
@ConfigurationProperties
注解属于Spring配置类注解,最先被加载
@FeignClient
注解,由 FeignClientsRegistrar
类实现了 ImportBeanDefinitionRegistrar
,通过 registerBeanDefinitions
方法向容器中注入 @FeignClient
注解的接口
@Configuration
, @Component
,属于配置类,Spring自动加载
根据Spring加载Bean的逻辑,这些注解都在Spring的可见范围内,是可以通过递归解析来分析这些实例的依赖关系,并在合适的时机实例化。
这么分析的话,问题可能是出在构造器上, 构造器的显示声明,替代了Spring在解析这些实例的时候所依据的递归解析方式,造成了这些实例不能被依赖解析所解决, 如果不使用构造器,那么我觉得这些注解所声明的类是可以放在Spring的依赖解析来处理的。
1,手动创建拦截器实例实现
拦截器不加任何自动配置注解,防止Spring自动装配和初始化,并通过构造器显示声明依赖
拦截器配置类通过构造器和@Autowired方式,准备好拦截器需要的bean
执行顺序3的时候,通过@Bean方式手动创建拦截器实例,把拦截器的初始化交给Spring。
这是没更改构造器方式去注入的,实践可行。
2,让Spring容器管理拦截器创建
这是去除了构造器,用注解来替代手动创建拦截器实例,实践可行。
拦截器创建可以不通过手动创建,而是交给Spring的容器管理来管理。
参考文档
www.cnblogs.com/shamo89/p/8…
stackoverflow.com/questions/2…