Spring AI Alibaba 1.1.2.0 实操完整记录:基础对话、流式 SSE、自建 MCP服务端互通实践
近期基于 Spring AI Alibaba 1.1.2.0 新版本开展实操学习,全程完整记录搭建流程、代码实现、踩坑现象与个人实操理解。本文全部内容均为本地实际运行验证后的一手记录,选用新版本核心原因是 Spring AI 各版本 API 改动幅度极大,旧版本教程参考价值较低,优先跟进新版可规避大量 API 废弃、配置失效问题。 在 AI 大模型落地场景中,大模型原生能力存在边界,企业私有化业务数据、内部工具、第三方接口无法直接让大模型访问,行业内主流两种解决方案:Function Calling、MCP 协议工具调用。 Function Calling 适用于简单本地工具封装,工具与 AI 服务耦合在同一项目;而 MCP(Model Context Protocol)支持将工具独立抽离为单独服务,实现工具服务与 AI 对话服务解耦、可多服务共享、统一注册管理,支持公共 MCP 服务直接接入使用。本次实操重点完成手动自建 MCP 服务端,并搭配 Spring AI Alibaba 实现基础对话、流式输出、跨服务 MCP 工具远程调用全流程落地。
一. 创建项目
1.1 参考页面
java2ai.com/docs/versio… 补充说明:该页面可查看 Spring AI Alibaba、原生 Spring AI、Spring Boot 三者版本对应关系,实操前务必核对版本匹配,版本不匹配会出现依赖缺失、MCP 相关 Bean 无法自动注入、SSE 流式连接失败等问题。本次选用组合:spring-ai-alibaba 1.1.2.0、spring ai 1.1.2、spring boot 3.5.16,三者完全适配。
1.2 创建项目
1.2.1 新建 SpringAIAlibaba-Demo 项目
补充:整体采用多模块 Maven 聚合工程结构,父工程统一管控全部依赖版本,避免子模块重复定义版本号,降低版本冲突风险。
1.2.2 创建SpringAI-MCP-Server和SprigAI-Demo
模块分工说明:
- SpringAI-Demo:客户端模块,对接阿里百炼通义千问大模型,实现对话、流式、记忆、MCP 客户端调用;
- SpringAI-MCP-Server:独立 MCP 工具服务端,封装天气查询工具,对外提供标准化 MCP 工具调用能力;
- 父工程 SpringAIAlibaba-Demo:统一管理 bom、jdk、编码等公共配置。
1.2.3 配置 SpringAIAlibaba
配置版本,spring-al-alibaba 使用比较新的 1.1.2.0 版本,对应 spring ai 版本 1.1.2,spring boot 版本 3.5.16。因为是学习,尽量使用比较新的版本,因为 ai 每个版本的变化都比较大,有时前面刚学会的,后面就变得没太大价值了。
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
<springboot.version>3.5.16</springboot.version>
<!-- SpringAi 的版本号 -->
<spring-ai.version>1.1.2</spring-ai.version>
</properties>
补充注意点:JDK 强制要求 17 及以上,Spring Boot 3.x 不再支持 JDK8,本地运行前需确认环境 JDK 版本,否则直接编译报错。
1.2.4 统一版本管控 dependencyManagement
统一引进版本
<dependencyManagement>
<dependencies>
<!-- SpringAi Alibaba 的版本管理 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringAi 的版本管理 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-extensions-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
补充说明:引入三层 bom 后,子模块引入对应 starter 依赖时无需指定 version,自动继承父工程统一版本,杜绝子模块依赖版本混乱、jar 包冲突、类找不到等问题。
1.2.5 SpringAI-Demo 基础配置
pom.xml 的 dependencies
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${springboot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<!-- 阿里百炼大模型服务平台 -->
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>
</dependencies>
补充依赖说明:
- spring-boot-starter-web:基础 web 服务,用于普通同步对话接口;
- dashscope starter:阿里百炼通义大模型对接核心依赖,自动装配 DashScopeChatModel;
- mcp-client-webflux:MCP 客户端依赖,基于 webflux SSE 协议连接远程 MCP 服务端,流式长连接必备。
application.yml
server:
port: 8311
logging: # SpringBoot日志配置
level:
# ChatClient 监控日志的等级
org.springframework.ai.chat.client.advisor: debug
spring:
ai:
dashscope:
# 阿里百炼大模型服务平台 API-Key申请文档
# https://help.aliyun.com/zh/model-studio/get-api-key
api-key: sk-****
chat:
options:
model: qwen-max
补充配置踩坑点:
- api-key 禁止硬编码提交代码至仓库,生产环境建议通过环境变量、配置中心注入;
- model 可按需替换 qwen-turbo、qwen-plus 等规格,不同模型工具调用能力、响应速度存在差异;
- advisor 日志设为 debug,调试对话流程、工具调用链路时可打印完整请求、返回报文,排查问题效率更高。
Springboot 启动
package vip.wayhua.ivy.ai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AIDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AIDemoApplication.class, args);
}
}
启动
二. 增加 Controller 调用测试
2.1 增加 ChatController
2.1.1 两种 Chat
Spring AI 有 ChatModel 和 ChatClient,ChatModel 是标准的方式调用,而 ChatClient 是对 ChatModel 进行了一次封装,使用起来方便很多。同时这两个都是通过配置注入的。
package vip.wayhua.ivy.ai.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatController {
private final ChatModel chatModel;
private final ChatClient chatClient;
public ChatController(ChatModel chatModel, ChatClient.Builder builder) {
this.chatModel = chatModel;
this.chatClient = builder.build();
}
}
补充二者适用场景区分:
- ChatModel:底层原生 API,流程透明,适合学习底层调用逻辑、自定义完整 Prompt 组装、精细控制请求参数;
- ChatClient:高层链式封装,代码简洁,内置记忆、工具、拦截器快捷 API,业务开发优先选用。
2.1.2 ChatModel 标准调用
调用方式 message-> UserMessage-> Prompt-> call (prompt)-> 返回 ChatResponse
这是最简单的调用,并且是阻塞式的,首先是要了解调用流程
@GetMapping("/simple/chat")
public String simpleChat() {
// 返回结果变量
String res = "";
//用户输入
String message = "在咖啡馆里,想要杯星巴克";
UserMessage userMessage = new UserMessage(message);
//提示词
Prompt prompt = new Prompt(userMessage);
//调用大模型并获取响应(文本响就)
ChatResponse chatResponse = chatModel.call(prompt);
if (chatResponse.getResult() != null) {
if (chatResponse.getResult().getOutput() != null) {
res = chatResponse.getResult().getOutput().getText();
}
}
return res;
}
2.1.3 测试
http://127.0.0.1:8311/simple/chat
现在是阻塞方式,所以时间比较长,5.59s。
补充:单次响应耗时受网络、模型负载、输入文本长度影响,多次测试耗时会浮动。
2.1.4 ChatClient 方式调用
ChatClient 调用就比较简单,都是链式调用
@GetMapping("/simple/chatclient")
public String simpleChatByChatClient() {
String res = "";
//构建prompt -> 发送到大模型 -> 获取大模型返回
//用户输入
String message = "在咖啡馆里,想要杯星巴克";
//链式调用
res = this.chatClient
.prompt(message) //构建prompt
.call() //发送到大模型
.content() //获取大模型文本返回
;
return res;
}
2.1.5 分析源码
两者效果差不多,主要在于调用,可以直接传入 message。要想了解更多,可以查看源码。
public ChatClientRequestSpec prompt(String content) {
Assert.hasText(content, "content cannot be null or empty");
return prompt(new Prompt(content));
}
再查看 Prompt
public Prompt(String contents) {
this(new UserMessage(contents));
}
由于可见 ChatClient 就是封装了 ChatModel 的,主要是方便调用,效率提升可不一定,刚才测试是 9s。再测试试一下。
补充耗时差异原因:ChatClient 内部增加参数校验、默认拦截器执行逻辑,少量额外开销,单次短文本对话差距明显;长文本、工具调用场景下开销占比可忽略。
2.1.6 测试 ChatClient
两次测试,确实调用时间要比 ChatModel 的方式时间长。
2.2 流式返回 Controller
流式返回就全部使用 ChatClient,虽然有时间长的问题,但是流式返回就可以忽略了。
流式返回,要配置 produces
@GetMapping(value = "/simple/stream",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat() {
String message = "在咖啡馆里,想要杯星巴克";
//链式调用
return this.chatClient
.prompt(message) //构建prompt
//.call() //发送到大模型
.stream() //以流式响应的方式和大模型进行交互
.content() //获取大模型文本返回
;
}
补充流式优势:SSE 长连接分段返回文字,前端实时展示打字效果,用户无等待卡顿感;底层基于 WebFlux 异步非阻塞,不占用容器同步线程,支持高并发。
2.3 SSE 定时发送空数据
/**
* 基于SSE
* @return
*/
@GetMapping(value = "/flux/interval",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> sseToFrontPage() {
return Flux.interval(Duration.ofSeconds(1)) //每1秒发送SSE协议消息
//将Map结构组装SSE协议格式消息,
//.map(seq->"{data:{},event:{},id:{}}\\n\\n") ;
//通过 ServerSentEvent 组装 SSE协议格式
.map(seq->ServerSentEvent
.<String>builder()
.event("")
.data("")
.id("")
.build())
;
}
这个是后面的内容。
每秒接收一条空数据
2.4 保存对话到内存
@GetMapping(value = "/chat/memory",produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatMemory(
@RequestParam(value="message") String message,
@RequestParam(value="chatId") String chatId
) {
//构建prompt -> 发送到大模型 -> 获取大模型返回
//链式调用
return this.chatClient
.prompt(message) //构建prompt
/* **********************
*
* advisors() 相当于Spring AOP
* 针对单个的 ChatClient 实例
*
* 1. 请求信息的重复性工作,安全信息验证
* 2. 返回信息的格式化(json格式)
* 3. 监控(请求以及返回信息的网络延迟,网络错误), 写入日志
*
* 内置拦截器 (advisor 机制):
*
* SimpleLoggerAdvisor:对话监控日志开启
* MessageChatMemoryAdvisor:多轮对话的上下文记忆开启
*
* 自定义拦截器:继承CallAdvisor, StreamAdvisor
*
*
* 对话管理:
*
* ChatMemory 对话历史管理对象
* MessageWindowChatMemory 实现了 ChatMemory接口
* ChatMemoryRepository:历史对话存储方式 (接口)
* InMemoryChatMemoryRepository 实现了 ChatMemoryRepository接口 : 将对话储存在内存
*
*
* *********************/
.advisors(new Consumer<ChatClient.AdvisorSpec>() {
@Override
public void accept(ChatClient.AdvisorSpec advisorSpec) {
advisorSpec.param(ChatMemory.CONVERSATION_ID,chatId);
}
}) //拦截器
//.call() //发送到大模型
.stream() //以流式响应的方式和大模型进行交互
.content() //获取大模型文本返回
;
}
补充记忆模块不足与优化方案:
- 默认 InMemoryChatMemoryRepository 仅内存存储,服务重启对话记录全部丢失,生产需替换 RedisChatMemoryRepository 持久化会话;
- MessageWindowChatMemory 默认存在消息条数上限,可自定义窗口大小控制上下文长度,避免 token 超限;
- chatId 由前端传入区分不同用户会话,需做好参数防重复、非法字符校验。
三. MCP-Server
3.1 配置以及启动
3.1.1 application.yml
server:
port: 8301
logging: # SpringBoot日志配置
level:
# ChatClient 监控日志的等级
org.springframework.ai.chat.client.advisor: debug
spring:
application:
name: springai-mcp-server-weather
ai:
mcp:
server:
# MCP服务器名称
name: springai-mcp-server-weather
version: 0.0.1
type: SYNC
补充配置说明:
- name、version 为 MCP 服务唯一标识,客户端连接时匹配名称识别服务;
- type=SYNC 同步模式,适合工具简单、执行耗时短场景;长耗时工具可选用 ASYNC 异步模式。
3.1.2 pom 配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>vip.wayhua.ivy</groupId>
<artifactId>SpringAIAlibaba-Demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SpringAI-MCP-Server</artifactId>
<packaging>jar</packaging>
<name>SpringAI-MCP-Server</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${springboot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<!--
SpringAi 1.1 基于注解(Annotations)开发MCP
基于注解(Annotations) 引入依赖 MCP-Annotations 依赖包
spring-ai-starter-mcp-server-webflux 这个依赖包自动引入 MCP-Annotations 依赖包
spring-ai-starter-mcp-server-webflux 属于 SpringAi,
不属于 SpringAi Alibaba,
所以需要引入SpringAi
-->
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
</dependencies>
</project>
补充依赖要点:MCP 服务端强制依赖 WebFlux,基于 SSE 长连接通信,不能仅引入 spring-boot-starter-web,否则 MCP 服务暴露接口异常,客户端无法建立连接。
3.1.3 启动项目
package vip.wayhua.ivy.mcp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
}
3.1.4 查看
补充启动校验:日志打印 MCP Server 启动成功标识,代表工具服务注册完成,可等待客户端接入。
3.2 WeatherTool
在 tools 目录创建 WeatherTool。至于怎么获取温度不是我们这次的关键,流程搞通才是重要的。并增加日志,方便后面调用的时候查看。
package vip.wayhua.ivy.mcp.tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class WeatherTool {
private static final Logger log = LoggerFactory.getLogger(WeatherTool.class);
@Tool(name = "Temperature",
description = "获取指定城市当前时间的温度")
public String getTemperature(
@ToolParam String cityName
) {
log.error("getTemperature->cityName->" + cityName);
return cityName + "温度值是30度";
}
@Tool(description = "获取指定城市的紫外线值")
public String getUltraviolet(@ToolParam String cityName) {
log.error("getUltraviolet->cityName->" + cityName);
return cityName + "紫外线值:103";
}
}
补充注解规范:
- @Tool 的 description 描述必须清晰完整,大模型依靠描述判断何时调用该工具;描述模糊会导致模型不会主动触发工具调用;
- @ToolParam 建议补充参数说明,复杂参数可增加枚举、取值范围描述,提升工具调用准确性;
- 生产环境替换模拟返回,对接第三方气象 API,增加异常捕获、参数校验逻辑。
3.3 配置 Tools
package vip.wayhua.ivy.mcp.conf;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import vip.wayhua.ivy.mcp.tools.WeatherTool;
@Configuration
public class ToolsRegister {
@Bean
public ToolCallbackProvider toolList(WeatherTool weatherTool) {
return MethodToolCallbackProvider.builder().toolObjects(weatherTool).build();
}
}
只有配置过才可使用。
补充:多工具类场景可连续调用 toolObjects,支持一次性注册多个工具实例;遗漏该 Bean 会导致 MCP 服务无法向外暴露任何工具。
3.4 查看日志
补充日志解读:启动日志会打印所有注册成功的工具名称、入参、描述,可快速核对工具是否全部加载。
四. 调用 Mcp
4.1 demo 增加配置
mcp 中的配置,如下
server:
port: 8301
spring:
application:
name: springai-mcp-server-weather
ai:
mcp:
server:
# MCP服务器名称
name: springai-mcp-server-weather
version: 0.0.1
type: SYNC
调用时,也要增加
server:
port: 8311
logging: # SpringBoot日志配置
level:
# ChatClient 监控日志的等级
org.springframework.ai.chat.client.advisor: debug
spring:
ai:
dashscope:
# 阿里百炼大模型服务平台 API-Key申请文档
# https://help.aliyun.com/zh/model-studio/get-api-key
api-key: sk-b956cd85b6cd472bacb8827be99bdaa9
chat:
options:
model: qwen-max
# MCP 客户端
mcp:
client:
sse:
connections:
# MCP 服务端的名称
springai-mcp-server-weather:
url: http://localhost:8301
# 必须添加以下配置
toolcallback:
enabled: true
connections 下面配置 Mcp 服务的名称,对应前面的名称。url 可以直接指定服务 ip 和端口。
补充配置踩坑点:
- toolcallback.enabled 必须开启,否则客户端无法拉取远程 MCP 工具列表;
- 多 MCP 服务端可在 connections 下新增多个节点,实现多组工具统一接入;
- 线上环境 url 替换服务注册中心地址,不硬编码localhost。
4.2 增加调用接口
为了简单,就直接放在 ChatController 中。
@GetMapping("/mcp/weather/")
String generation(
@RequestParam(value = "message",defaultValue = "你有什么工具")
String message
) {
return this.chatClient
.prompt(message)
.call()
.content();
}
访问:
http://127.0.0.1:8311/mcp/weather/
你有什么工具,并没有显示 mcp,是因为没有引入。
4.3 ToolCallbackProvider
在 ChatController 中引用 ToolCallbackProvider toolCallbackProvider
public ChatController(ChatModel chatModel,
ChatClient.Builder builder,
ToolCallbackProvider toolCallbackProvider) {
this.chatModel = chatModel;
this.chatClient = builder
.defaultToolCallbacks(toolCallbackProvider)
.build();
}
补充原理:MCP 客户端自动将远程工具封装为 ToolCallback,通过 defaultToolCallbacks 全局注入 ChatClient,所有对话请求自动携带远程工具列表供大模型选择调用。
4.4 运行
表示成功了,不知道为什么没看到 methods。
补充:日志未打印 methods 属于日志输出粒度问题,不影响工具实际调用,可开启 trace 级日志查看完整工具元数据。
4.5 再次询问你有什么工具
4.6 询问天气
询问天气,就是人为制造调用函数的机会。
查看日志
真实调用就不是我们要讲解的,流程完成。后面会讲如何发布到 nacos,再发现调用(我也不会,要学)。
补充后续拓展方向(当前未落地,后续学习计划):
- MCP 服务注册至 Nacos 配置中心,客户端通过服务名动态发现,无需硬编码 IP 端口;
- MCP 工具权限管控,不同 AI 客户端隔离可调用工具;
- MCP 调用链路监控、超时重试、熔断降级;
- 统一 MCP 工具市场,实现工具一键发布、订阅。
五. 小结
实操收获总结
- 基础调用层面:掌握 Spring AI Alibaba 对接阿里百炼通义大模型两种调用方式 ChatModel、ChatClient,分清二者封装层级、性能差异、适用场景;实现同步阻塞对话、SSE 流式输出、心跳保活、内存多轮对话记忆完整接口,理解 Advisor 拦截器机制在对话流程中的作用。
- MCP 架构层面:厘清 Function Calling 与 MCP 核心区别,完成独立 MCP 服务端搭建、基于 @Tool 注解开发远程工具、工具 Bean 注册、SSE 客户端连接远程 MCP 服务全流程,验证大模型自动识别并远程调用 MCP 工具的完整链路。
- 版本与工程层面:搭建多模块 Maven 聚合工程,通过三层 BOM 统一管控 Spring AI、Spring AI Alibaba、Spring Boot 版本,规避版本冲突,梳理新版本 API 改动带来的各类配置、依赖踩坑点。
当前方案存在不足
- 对话记忆仅内存存储,服务重启会话丢失,无持久化方案,无法支撑线上用户长期会话;
- MCP 服务地址硬编码在配置文件,无服务注册发现机制,多实例部署、服务扩容、IP 变更维护成本高;
- 工具均为模拟数据,无真实第三方接口对接,缺少参数校验、异常捕获、超时处理逻辑;
- 缺少统一全局异常处理,大模型调用失败、MCP 连接断开、工具执行报错无友好返回;
- 无鉴权逻辑,大模型 API 密钥明文配置、MCP 服务无访问权限控制,存在安全风险;
- 未实现工具调用日志持久化、调用耗时监控,线上问题排查缺少数据支撑。
后续学习落地计划
- 接入 Redis 实现 ChatMemory 持久化,自定义会话过期策略;
- 研究 MCP 结合 Nacos 注册中心,实现服务自动发现、动态配置;
- 完善工具层封装,增加接口调用异常、参数校验、熔断重试;
- 统一全局异常处理器,封装 AI 调用各类异常返回格式;
- 增加接口鉴权、密钥加密存储、MCP 服务访问权限校验;
- 接入监控组件,记录对话耗时、工具调用次数、失败率指标。
六. 代码
完整聚合工程代码分为父工程、AI 客户端模块、MCP 服务端模块三部分,包含全部 pom 配置、yml 配置、Controller、MCP 工具类、配置类,可直接导入本地运行,仅需替换百炼平台 api-key 即可验证全流程功能。