SpringBoot中@Autowired注入TokenService为空,带@Qualifier时失效求解决方案
问题原因分析
这是典型的循环依赖+Bean初始化顺序冲突导致的注入失败:
TokenServiceImpl的构造函数依赖名为rstProxy的RestTemplateBean;- 而
rstProxy这个RestTemplate的创建方法restTemplateWithProxy()中,又需要注入TokenService来初始化AuthorizationInterceptor; - Spring初始化Bean时,会先尝试创建
TokenServiceImpl,发现需要rstProxy,于是转而去创建rstProxy的RestTemplate;但创建RestTemplate时,TokenService还未完成初始化,此时Application类中@Autowired的tokenService还未被赋值,因此是null;最终整个循环依赖导致TokenServiceImpl无法正常初始化,Application里的tokenService自然也是null。
当你移除@Qualifier("rstProxy")后,TokenServiceImpl会使用其他可用的RestTemplate(比如默认的无代理实例),不会触发这个循环依赖链,因此注入正常。
解决方案(保留@Qualifier的前提下)
方案1:使用ObjectProvider延迟获取TokenService
ObjectProvider是Spring提供的用于延迟获取Bean的工具,能避免在Bean初始化阶段直接触发循环依赖:
修改Application类中的restTemplateWithProxy方法:
@Bean("rstProxy") public RestTemplate restTemplateWithProxy(ObjectProvider<TokenService> tokenServiceProvider) { final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); messageConverters.add(new MappingJackson2HttpMessageConverter()); final RestTemplate restTemplate = new RestTemplate(); List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); // 延迟获取TokenService,直到真正需要时才初始化 interceptors.add(new AuthorizationInterceptor(tokenServiceProvider.getObject())); restTemplate.setInterceptors(interceptors); restTemplate.getMessageConverters().addAll(messageConverters); restTemplate.setRequestFactory(factory); return restTemplate; }
方案2:将AuthorizationInterceptor注册为Spring Bean
把AuthorizationInterceptor交给Spring容器管理,利用Spring的三级缓存机制自动处理循环依赖:
- 给
AuthorizationInterceptor添加@Component注解,让Spring扫描并管理它:
@Component public class AuthorizationInterceptor implements ClientHttpRequestInterceptor { private final TokenService tokenService; // 构造器注入TokenService public AuthorizationInterceptor(TokenService tokenService) { this.tokenService = tokenService; } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { // 你的拦截逻辑 return execution.execute(request, body); } }
- 修改Application类的
restTemplateWithProxy方法,直接注入AuthorizationInterceptor:
@Bean("rstProxy") public RestTemplate restTemplateWithProxy(AuthorizationInterceptor authorizationInterceptor) { final List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); messageConverters.add(new MappingJackson2HttpMessageConverter()); final RestTemplate restTemplate = new RestTemplate(); List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); interceptors.add(authorizationInterceptor); restTemplate.setInterceptors(interceptors); restTemplate.getMessageConverters().addAll(messageConverters); restTemplate.setRequestFactory(factory); return restTemplate; }
方案3:使用@Lazy延迟初始化Bean
通过@Lazy注解打破循环依赖的初始化链,让Spring先完成其中一个Bean的初始化:
方式A:在TokenServiceImpl的RestTemplate参数上添加@Lazy
@Service public class TokenServiceImpl implements TokenService{ private final Settings settings; private final RestTemplate restTemplate; @Autowired public TokenServiceImpl(Settings settings, @Qualifier("rstProxy") @Lazy RestTemplate restTemplate) { this.settings= settings; this.restTemplate = restTemplate; } //... }
方式B:在Application类的TokenService注入上添加@Lazy
@SpringBootApplication @EnableAsync @EnableScheduling public class Application extends SpringBootServletInitializer { @Autowired @Lazy private TokenService tokenService; //... restTemplateWithProxy方法不变 }
@Lazy会让Spring生成一个代理对象,直到第一次真正使用时才初始化目标Bean,从而绕过初始化阶段的循环依赖。
内容的提问来源于stack exchange,提问作者user1814879
相关产品推荐
相关产品推荐

