第一章 ChatModel
Spring AI Alibaba官网: https://java2ai.com/docs/versions
第一节 父工程
整体项目目录结构

|
STEP1: 创建普通的Maven项目
注意:不要用SpringInintilizer去创建
STEP2: 修改pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>
<groupId>com.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
<spring-boot.version>3.5.5</spring-boot.version>
<spring-ai.version>1.0.0</spring-ai.version>
<SpringAIAlibaba.version>1.0.0.2</SpringAIAlibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<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-bom</artifactId>
<version>${SpringAIAlibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
第二节 子工程
STEP1: 创建子model
STEP2: 修改子pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA01_HelloWorld</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
STEP3: application.properties
- 位置: resources/application.properties
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_HelloWorld
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=qwen-plus
STEP4: SpringBoot的启动类
- 位置: java/com/mts/SAA01HelloWorldApplication.java
package com.mts;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SAA01HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(SAA01HelloWorldApplication.class, args);
}
}
STEP5: 配置类注入ChatModel
- 位置: java/com/mts/config/SaaLLMConfig.java
- 注意: 注意包! 注意包! 注意包! 看清楚是SpringAi的还是DasScope的
package com.mts.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder().apiKey(apiKey).build();
}
}
STEP6: 编写Controller进行测试
- 说明: 我这里前后端传递做了封装, 简单测试就返回String即可
- 位置: java/com/mts/Controller/ChatHelloController.java
package com.mts.Controller;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.mts.common.response.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/hello")
public class ChatHelloController {
@Autowired
private DashScopeChatModel dashScopeChatModel;
@GetMapping("/dochat")
public ResponseResult<String> doChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
String resContent = dashScopeChatModel.call(msg);
return ResponseResult.ok(resContent);
}
@GetMapping("/streamchat")
public Flux<String> streamChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
Flux<String> resStream = dashScopeChatModel.stream(msg);
return resStream;
}
}
第二章 调用本地模型Ollama
第一节 Ollama本地大模型部署
STPE1: 下载Ollama软件
官网: https://ollama.com/download/windows
STEP2: 配置环境变量
STEP3: 下载deepseek
ollama pull deepseek-r1:1.5b
第二节 Ollama常用命令
| 指令 |
作用 |
ollama pull xxx |
拉取模型 |
ollama ps |
后台正在运行的实例 |
ollama list |
查看下载的本地模型 |
ollama run xxx |
运行xxx模型在后台 |
ollama stop xxx |
停止xxx模型在运行 |
第三节 对接本地大模型
STEP1: 创建子model
STEP2: 修改子pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>com.mts</groupId>
<artifactId>untitled21</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA02_Ollama</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
STEP3: application.properties
- Ollama端口: 11434
- model的名称: 自己去
ollama list 查看
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_Ollama
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=deepseek-r1:1.5b
STEP4: SpringBoot的启动类
STEP5: 编写Controller进行测试
- 位置: java/com/mts/Controller/ChatHelloController.java
- 注意: SpringAIAlibaba有dashscopeChatModel和ollamaChatModel两个ChatModel(接口)实现, 分别对应 远程调用 和 本地调用, so要用Qualifier去指定
package com.mts.controller;
import com.mts.common.response.ResponseResult;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/ollama")
public class OllamaController {
@Autowired
@Qualifier("ollamaChatModel")
private ChatModel chatModel;
@GetMapping("/ollama/chat")
public ResponseResult<String> chat(@RequestParam(name = "message") String msg) {
String result = chatModel.call(msg);
return ResponseResult.ok(result);
}
@GetMapping("/ollama/streamchat")
public Flux<String> streamchat(@RequestParam(name = "message", defaultValue = "你是谁") String msg) {
return chatModel.stream(msg);
}
}
STEP6: cmd运行Ollama模型
ollama run deepseek-r1:1.5b
第三章 ChatClient
第一节 ChatClient 🆚 ChatModel
|
ChatModel |
ChatClient |
| 注入形式 |
自动注入 |
手动注入且依赖ChatModel,使用自动配置的 ChatClient.Builder |
| 调用方式 |
直接调用call()或者stream()方法 |
链式调用,支持同步和反应式(Reactive)编程模型,自动封装提示词和解析响应 |
| 结构化输出 |
需手动解析响应文本 |
支持自动映射为java对象 |
| 适用场景 |
实现简单功能和场景 |
快速开发复杂功能的场景,如企业级智能XXX问答系统 |
| 功能扩展 |
偏弱 |
强,支持聊天记忆(Chat Memory)/工具Tool/函数调用(Function Calling)/RAG等等 |
第二节 如何获取ChatClient
 有两个默认实现(①远程模型 ②本地模型) |
 这个Default其实是null来的 |

|
@Autowired
private DashScopeChatModel dashScopeChatModel;
private ChatClient chatClient;
第三节 通过构造方法Builder注入
- 分析: 可以看到ChatClient不是
@Autowired注入的, 而是构造方法中build出来的!
package com.mts.Controller;
import com.mts.common.response.ResponseResult;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/chatclient")
public class ChatClientController {
private ChatClient chatClient;
public ChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
}
@GetMapping("/dochat")
public ResponseResult<String> doChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
String resContent = chatClient.prompt().user(msg).call().content();
return ResponseResult.ok(resContent);
}
@GetMapping("/streamchat")
public Flux<String> streamChat(@RequestParam(value = "message", defaultValue = "你是谁") String msg) {
Flux<String> resContent = chatClient.prompt().user(msg).stream().content();
return resContent;
}
}
第四节 通过配置类注入ChatClient
- 易混: 你第一章/第一节/STEP5不也写的”配置类注入ChatModel”吗? 那不一样? ChatChatModel也需要注入?
- 回答: 不不不! 那里至少注入apikey, 让DashScopChatModel拿个apikey; 而这里是用配置类build一个ChatClient然后注入Bean
- 位置: java/com/mts/config/SaaLLMConfig.java 【共计4处修改】
package com.mts.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder().apiKey(apiKey).build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
- 位置: 任意Controller中;
- 如何用: 直接
@Autowired即可
@Autowired
private ChatClient dashScopechatClientv2;
第四章 多模型共存
第一节 ChatModel配置多模型共存
STEP1: 创建子Model
STEP2: 修改子pom.xml 【同第一章】
STEP3: applicant.properties
- 位置: resourcesapplicant/properties 【删除两行】
- 分析: 因为是多个模型, 所以不在配置文件指定
server.port=8080
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA01_HelloWorld
spring.ai.dashscope.api-key=sk-6bb83948db224d3abcbd03f24be05228
STEP4: SpringBoot启动类
STEP5: 配置类注入多个ChatModel
- 位置: java/com/mts/config/SaaLLMConfig.java
package com.mts.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
private final String DEEPSEEKV3_2 = "deepseek-v3.2";
private final String DEEPSEEKVR1 = "deepseek-r1";
@Bean
public ChatModel deepSeekV3_2() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKV3_2).build())
.build();
}
@Bean
public ChatModel deepSeekR1() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKVR1).build())
.build();
}
}
STEP6: 编写Controller进行测试
- 位置: java/src/main/java/com/mts/controller/MulChatModelController.java
package com.mts.controller;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/mulchatmodel")
public class MulChatModelController {
@Autowired
@Qualifier("deepSeekV3_2")
private ChatModel deepSeekV3_2;
@Autowired
@Qualifier("deepSeekR1")
private ChatModel deepSeekR1;
@GetMapping("/v3stream")
public Flux<String> v3modelChat(@RequestParam(value = "message") String msg) {return deepSeekV3_2.stream(msg);}
@GetMapping("/r1stream")
public Flux<String> r1modelChat(@RequestParam(value = "message") String msg) {return deepSeekR1.stream(msg);}
}
第二节 ChatClient配置多模型共存
STEP5: 配置类注入多个ChatChatClient
- 位置: 同上【共11行修改】
- 注意: 要现有ChatModel再有ChatClient
package com.mts.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
private final String DEEPSEEKV3_2 = "deepseek-v3.2";
private final String DEEPSEEKVR1 = "deepseek-r1";
@Bean
public ChatModel deepSeekV3_2() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKV3_2).build())
.build();
}
@Bean
public ChatModel deepSeekR1() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEKVR1).build())
.build();
}
@Bean
public ChatClient deepSeekV3_2ChatModel(@Qualifier("deepSeekV3_2") ChatModel deepSeekV3_2ChatModel) {
return ChatClient.builder(deepSeekV3_2ChatModel)
.defaultOptions(ChatOptions.builder().model(DEEPSEEKV3_2).build())
.build();
}
@Bean
public ChatClient deepSeekR1ChatModel(@Qualifier("deepSeekR1") ChatModel deepSeekR1ChatModel) {
return ChatClient.builder(deepSeekR1ChatModel)
.defaultOptions(ChatOptions.builder().model(DEEPSEEKVR1).build())
.build();
}
}
STEP6: 编写Controller进行测试
package com.mts.controller;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/mulchatclient")
public class MulChatClientController {
@Autowired
@Qualifier("deepSeekV3_2ChatModel")
private ChatClient deepSeekV3_2ChatModel;
@Autowired
@Qualifier("deepSeekR1ChatModel")
private ChatClient deepSeekR1ChatModel;
@GetMapping("/v3stream")
public Flux<String> v3modelChat(@RequestParam(value = "message") String msg) {
return deepSeekV3_2ChatModel.prompt().user(msg).stream().content();
}
@GetMapping("/r1stream")
public Flux<String> r1modelChat(@RequestParam(value = "message") String msg) {
return deepSeekR1ChatModel.prompt().user(msg).stream().content();
}
}
第