Spring Boot与Spring AI的结合为开发者提供了一条快速构建智能应用的捷径。作为一名长期使用Spring生态的开发者,我最近完整走通了这套技术栈的整合流程,在这里分享从环境搭建到第一个AI功能上线的完整实践。
这个组合最吸引我的地方在于:Spring Boot的自动化配置能省去大量基础工作,而Spring AI则把复杂的AI模型调用封装成了简单的API。即使没有专业的机器学习背景,也能在半小时内让应用具备基础的AI能力。下面我会详细拆解每个关键环节的实现过程。
推荐使用以下环境组合:
在pom.xml中添加关键依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>0.7.1</version>
</dependency>
注意:Spring AI目前仍处于experimental阶段,版本更新较快,建议在官方仓库确认最新版本号。
使用Spring Initializr创建项目时,建议选择:
创建后检查application.properties:
properties复制# 设置服务端口
server.port=8080
# 启用Spring Boot的Actuator端点(可选)
management.endpoints.web.exposure.include=health,info
以OpenAI为例,首先添加专属依赖:
xml复制<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1</version>
</dependency>
然后在配置文件中添加API密钥:
properties复制spring.ai.openai.api-key=your-api-key
spring.ai.openai.model=gpt-3.5-turbo
创建ChatController:
java复制@RestController
@RequiredArgsConstructor
public class ChatController {
private final OpenAiChatClient chatClient;
@GetMapping("/chat")
public String generate(@RequestParam String message) {
return chatClient.call(message);
}
}
测试方法:
bash复制curl "http://localhost:8080/chat?message=用Java写一个快速排序"
对于需要精细控制的场景,可以配置PromptTemplate:
java复制PromptTemplate template = new PromptTemplate("""
你是一个专业的{role},请用{style}风格回答:
{question}
""");
Map<String, Object> params = Map.of(
"role", "Java架构师",
"style", "简洁专业",
"question", "解释Spring Bean的生命周期"
);
String result = chatClient.call(template.render(params));
实现一个能解析PDF并回答问题的服务:
关键依赖新增:
xml复制<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.27</version>
</dependency>
文档解析服务:
java复制@Service
public class PdfService {
public String extractText(MultipartFile file) throws IOException {
try (PDDocument doc = PDDocument.load(file.getInputStream())) {
return new PDFTextStripper().getText(doc);
}
}
}
AI处理层:
java复制@Service
@RequiredArgsConstructor
public class AiService {
private final OpenAiEmbeddingClient embeddingClient;
private final OpenAiChatClient chatClient;
public List<Double> createEmbedding(String text) {
return embeddingClient.embed(text);
}
public String answerQuestion(String context, String question) {
String prompt = """
基于以下上下文回答问题:
{context}
问题:{question}
""";
PromptTemplate template = new PromptTemplate(prompt);
Map<String, Object> params = Map.of(
"context", context,
"question", question
);
return chatClient.call(template.render(params));
}
}
REST控制器示例:
java复制@PostMapping("/analyze")
public String analyzeDocument(
@RequestParam MultipartFile file,
@RequestParam String question) throws IOException {
String text = pdfService.extractText(file);
return aiService.answerQuestion(text, question);
}
使用Spring Cache减少API调用:
java复制@Cacheable(value = "embeddings", key = "#text.hashCode()")
public List<Double> createEmbedding(String text) {
return embeddingClient.embed(text);
}
配置Redis缓存:
properties复制spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379
配置Resilience4j进行限流:
java复制@Bean
public CircuitBreakerConfigCustomizer circuitBreakerConfig() {
return config -> config
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofSeconds(30))
.slidingWindowSize(10);
}
@CircuitBreaker(name = "aiService")
public String answerQuestionWithFallback(String context, String question) {
// 主逻辑...
}
通过Actuator暴露关键指标:
properties复制management.endpoints.web.exposure.include=health,info,metrics
management.metrics.enable.ai.requests=true
自定义指标采集:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "spring-ai-demo"
);
}
典型错误:
code复制org.springframework.web.client.ResourceAccessException:
I/O error on POST request for "https://api.openai.com/v1/chat/completions":
Connection timed out
解决方案:
properties复制spring.ai.openai.connect-timeout=30s
spring.ai.openai.read-timeout=60s
当遇到"maximum context length"错误时:
java复制public List<String> chunkText(String text, int maxLength) {
return Arrays.stream(text.split("\\s+"))
.collect(Collectors.groupingBy(
s -> (s.length() - 1) / maxLength,
Collectors.joining(" ")
))
.values()
.stream()
.toList();
}
处理大文档时的建议:
bash复制java -Xmx1024m -jar your-app.jar
通过抽象接口实现灵活切换:
java复制public interface AiProvider {
String generateText(String prompt);
List<Double> createEmbedding(String text);
}
@Service
@Profile("openai")
public class OpenAiProvider implements AiProvider {
// 实现方法...
}
@Service
@Profile("anthropic")
public class ClaudeAiProvider implements AiProvider {
// 实现方法...
}
自定义模型配置示例:
properties复制spring.ai.openai.fine-tuned-model=ft:your-model-id
对应的调用代码:
java复制@Value("${spring.ai.openai.fine-tuned-model}")
private String fineTunedModel;
public String callFineTunedModel(String input) {
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withModel(fineTunedModel)
.withTemperature(0.3f)
.build();
return chatClient.call(new Prompt(input, options));
}
实现天气查询功能:
java复制@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
return request -> {
// 调用真实天气API
return new WeatherResponse(...);
};
}
@Bean
public FunctionCallback weatherFunctionCallback() {
return new FunctionCallbackWrapper<>(
"getCurrentWeather",
"获取指定位置的当前天气",
weatherFunction(),
new JsonSchemaConverter()
);
}
在实际项目中,我发现Spring AI最大的价值在于它大幅降低了AI能力的接入门槛。通过合理的架构设计,完全可以在现有Spring应用中渐进式地引入AI功能。对于生产环境,建议重点关注:API调用的稳定性保障、结果的缓存策略以及成本控制机制。