把
spring-ai-openai-spring-boot-starter和spring-boot-starter-webflux放一個項目,啟動大概率報No qualifying bean of type 'RestClient.Builder'。根因是早期 OpenAI 自動設定寫死了要注入RestClient.Builder,而 WebFlux 只給WebClient.Builder。這是 1.0.0-M1 到 M3 的老 bug,1.0.0 GA 已修;卡在 M 版就臨時補一個 RestClient 的 Bean。
Issue & Env(案發現場與環境)
- 業務場景:用 WebFlux(reactive 棧)搭的項目,想接入 Spring AI 的 OpenAI 模型。
- 環境:Spring AI 1.0.0-M1~M3(bug 區間),Spring Boot 3.3.4,Java 17。1.0.0 GA 之後已修復。
StackTrace(堆疊日誌)
啟動報:
APPLICATION FAILED TO START
Description:
Parameter 2 of method openAiChatModel in org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration required a bean of type 'org.springframework.web.client.RestClient$Builder' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.web.client.RestClient$Builder' in your configuration.
底層例外鏈的完整堆疊,頂層是啟動時構造 Bean 失敗的 UnsatisfiedDependencyException,根因是找不到 RestClient.Builder:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'openAiChatModel' defined in class path resource [org/springframework/ai/autoconfigure/openai/OpenAiAutoConfiguration.class]: Unsatisfied dependency expressed through method 'openAiChatModel' parameter 2: No qualifying bean of type 'org.springframework.web.client.RestClient$Builder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1217)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.web.client.RestClient$Builder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1512)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1432)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:908)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:793)
Reproduce(復現程式碼)
兩個依賴單獨存進 WebFlux 項目就能穩定復現:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
配好 spring.ai.openai.api-key 後啟動,直接報上面的 RestClient.Builder 缺失。
Root Cause(根因分析)
自動設定裡 openAiChatModel(...) 的第 3 個參數(parameter 2)寫死了 RestClient.Builder。構造 Bean 時從容器找這個類型的 Bean,WebFlux 項目裡沒有,直接 UnsatisfiedDependencyException。RestClient 是 Spring MVC(servlet 棧)的 HTTP 客戶端;WebFlux 項目只自動設定 WebClient 和 WebClient.Builder,不註冊 RestClient.Builder。
維護者在 issue 裡把取捨說清了。markpollack 試過直接給 spring-ai-openai 拉 spring-boot-starter-web,能順帶把 RestClient.Builder 設定出來,但會把 Tomcat、spring-webmvc 全拖進來,控制台應用不想要。wilkinsona 給的方向:依賴只加 spring-web,注入點改用 ObjectProvider,用 AnyNestedCondition 判斷「有 RestClient.Builder 或有 WebClient.Builder」二選一即可。這個方向最終落在 PR #1550,有人在 1.0.0-SNAPSHOT 上驗證過已恢復。
The Fix(解決方案)
正解:升級到 Spring AI 1.0.0 GA 或更高
1.0.0 起自動設定在 servlet、reactive 兩種棧下都能正確裝配。用 BOM 管版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
再把 starter 換成對應版本的標準命名,比如 spring-ai-starter-model-openai。
卡在 M 版的臨時繞過
沒法升級時,補一個 RestClient.Builder Bean 就能讓自動設定過:
@Bean
RestClient.Builder restClientBuilder() {
return RestClient.builder();
}
或者給 spring-ai-openai 拉一個 spring-web 依賴,讓 RestClient 自動設定生效。別用 spring-boot-starter-web 去解,它會把整套 servlet 棧帶進來,reactive 項目不划算。
