Spring Boot应用出现Bean循环引用错误的原因及修复方法
循环依赖产生原因
@Configuration标注的类本身也是Spring托管的Bean,其完整初始化流程为:实例化 -> 依赖注入 -> 执行@PostConstruct生命周期回调 -> 完成初始化- 你的代码中
@PostConstruct标注的startMonitoring方法会调用fetchWeatherData,而fetchWeatherData直接调用了同类下的@Bean方法restTemplate() - Spring对
@Configuration类会做CGLIB代理,调用类内部的@Bean方法默认会尝试从Spring容器中获取对应Bean实例。此时要创建RestTemplate,需要先保证其定义所在的配置类AppConfig已经完成全量初始化,但AppConfig还卡在@PostConstruct的执行流程中,二者形成依赖循环。
修复方案
方案1:注入RestTemplate实例而非直接调用@Bean方法
将RestTemplate作为AppConfig的依赖注入,避免在初始化阶段触发RestTemplate的创建逻辑:
@Configuration public class AppConfig { @Autowired private Environment env; // 提前注入RestTemplate实例 @Autowired private RestTemplate restTemplate; private List<Weather> weathers = new ArrayList<>(); @Bean public RestTemplate restTemplate() { var factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(3000); factory.setReadTimeout(3000); return new RestTemplate(factory); } @PostConstruct public void startMonitoring() { // 原有逻辑不变 String url = env.getProperty("openWeatherUrl"); String apiKey = env.getProperty("openWeatherApiKey"); try (InputStream resource = app.class.getResourceAsStream("/config.csv")) { CSVReader reader = new CSVReader(new InputStreamReader(resource)); String[] lineInArray; while ((lineInArray = reader.readNext()) != null) { Weather weather = new Weather(); if (!lineInArray[0].contains("city")) { weather.setCity(lineInArray[0]); } else { continue; } if (!lineInArray[1].contains("temperaturesLimit")) { weather.setTemperaturesLimit(Float.parseFloat(lineInArray[2])); } if (!lineInArray[2].contains("temperaturesLimit")) { weather.setFrequency(Integer.parseInt(lineInArray[2])); } URI uri = new URI(url + "?q=" + weather.getCity() + "&units=metric&APPID=" + apiKey); weather.setUri(uri); weathers.add(weather); } } catch (IOException | CsvValidationException | URISyntaxException e) { System.out.println(e.getMessage()); } fetchWeatherData(); } void fetchWeatherData(){ // 直接使用注入的restTemplate实例,不调用@Bean方法 weathers.forEach(weather -> { var res = restTemplate.getForEntity(weather.getUri(), Weather.class); }); } }
方案2:延迟执行初始化逻辑到容器完全启动后
将startMonitoring方法的触发时机改为容器全量初始化完成后,避免在Bean初始化阶段调用@Bean方法:
// 移除@PostConstruct注解,改用@EventListener监听容器刷新完成事件 @EventListener(ContextRefreshedEvent.class) public void startMonitoring() { // 原有逻辑保持不变 }
方案3:拆分配置类
将RestTemplate的Bean定义移动到独立的配置类中,避免RestTemplate的创建依赖当前AppConfig的初始化状态。
内容的提问来源于stack exchange,提问作者Masi Boo
相关产品推荐
相关产品推荐

