新来的技术总监,把DDD落地的那叫一个高级优雅!
(??金石瓜分计划强势上线,速戳上图了解详情??)引言随着人工智能的飞速发展,大语言模型 (LLM) 正在革命性地重塑用户与软件的交互范式。想象一下这样的场景:用户无需钻研复杂的 API 文档或者在繁琐的表单间来回切换,只需通过自然语言直接与系统对话——"帮我查找所有 2023 年出版的图书"、" 创建一个新用户叫张三,邮箱是zhangsan@example.com[1]"。这种直观、流畅的交互方式不仅能显著降低新用户的学习曲线,更能大幅削减 B 端系统的培训成本和实施周期,让企业应用变得更为简单和高效。
这正是「Model Context Protocol (MCP)」协议在应用层面所带来的价值体现。
认识 MCP我这里不粘贴官方的定义,用大白话给大家解释下:MCP 就像是 AI 世界的 "万能适配器"。想象你有很多不同类型的服务和数据库,每个都有自己独特的 "说话方式"。AI 需要和这些服务交流时就很麻烦,因为要学习每个服务的 "语言"。
「MCP」解决了这个问题 - 它就像一个统一的翻译官,让 AI 只需学一种 "语言" 就能和所有服务交流。这样开发者不用为每个服务单独开发连接方式,AI 也能更容易获取它需要的信息。
如果你是一个后端同学,那么应该接触或听说过「gRPC」。「gRPC」通过标准化的通信方式可以实现不同语言开发的服务之间进行通信,那么「MCP」专门为 AI 模型设计的 "翻译官和接口管理器",让 AI 能以统一方式与各种应用或数据源交互。
我们假设开发了一个天气服务,用户想要查询深圳的天气,这里分别以传统「API」方式和「MCP」方式进行对比:
对现有 Spring Boot 服务改造这里为了演示,先准备好一个图书管理服务,图书实体字段如下:
importjakarta.persistence.*;importjakarta.validation.constraints.NotBlank;importjakarta.validation.constraints.NotNull;importjakarta.validation.constraints.PastOrPresent;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;
importjava.time.LocalDate;
@Entity@Table(name ="books")@Data@AllArgsConstructor@NoArgsConstructorpublicclassBook{
@Id@GeneratedValue(strategy = GenerationType.IDENTITY)privateLong
@NotBlank(message ="书名不能为空")@Column(nullable = false)privateString title;
@NotBlank(message ="分类不能为空")@Column(nullable = false)privateString category;
@NotBlank(message ="作者不能为空")@Column(nullable = false)privateString author;
@NotNull(message ="出版日期不能为空")@PastOrPresent(message ="出版日期不能是未来日期")@Column(nullable = false)privateLocalDate publicationDate;
@NotBlank(message ="ISBN编码不能为空")@Column(nullable = false, unique = true)privateString isbn;
}为这个服务编写了 2 个测试方法:
importcom.example.entity.Book;
importjava.util.List;
publicinterfaceBookService{
// 根据作者查询ListBookfindBooksByAuthor(Stringauthor);
// 根据分类查询ListBookfindBooksByCategory(Stringcategory);}现在我们要将这个 SpringBoot 服务改造成 MCP 服务,需要以下步骤:
1. 导入依赖在「pom.xml」中引入相关依赖,这里提示一下 anthropic 的访问需要代理,否则会提示 403。
!-- Spring AI 核心依赖 --dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-core/artifactId/dependency
!-- Anthropic 模型支持 --dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-anthropic-spring-boot-starter/artifactId/dependency
!-- MCP 服务器支持 - WebMVC版本 --dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-webmvc-spring-boot-starter/artifactId/dependency由于目前这些依赖还是预览版本,所以在「Maven」中央仓库中是找不到的,需要我们额外引入仓库地址。
repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository repository idspring-snapshots/id nameSpring Snapshots/name urlhttps://repo.spring.io/snapshot/url releases enabledfalse/enabled /releases /repository repository nameCentral Portal Snapshots/name idcentral-portal-snapshots/id urlhttps://central.sonatype.com/repository/maven-snapshots//url releases enabledfalse/enabled /releases snapshots enabledtrue/enabled /snapshots /repository/repositories关于项目中代理的配置可以参考我这段配置:
importjakarta.annotation.PostConstruct;importorg.springframework.context.annotation.Configuration;
@ConfigurationpublicclassProxyConfig{
// 代理设置privatefinalStringPROXY_HOST="127.0.0.1";privatefinal intPROXY_PORT=10080;
@PostConstructpublicvoidsetSystemProxy() { // 设置系统代理属性,这会影响Spring Boot自动配置的HTTP客户端 System.setProperty("http.proxyHost",PROXY_HOST); System.setProperty("http.proxyPort",String.valueOf(PROXY_PORT)); System.setProperty("https.proxyHost",PROXY_HOST); System.setProperty("https.proxyPort",String.valueOf(PROXY_PORT));
System.out.println("System proxy configured: http://"+PROXY_HOST+":"+PROXY_PORT); }}2. 引入配置我们的目的是将一个 Spring 服务改造成 MCP 服务,所以这里不需要进行客户端的配置,同理,在引入依赖的时候也不用引入客户端的依赖。
# Spring AI api-keyspring.ai.anthropic.api-key=这里换成你的api-key
# MCP服务端开启spring.ai.mcp.server.enabled=true
# MCP服务端配置spring.ai.mcp.server.name=book-management-serverspring.ai.mcp.server.version=1.0.0spring.ai.mcp.server.type=SYNCspring.ai.mcp.server.sse-message-endpoint=/mcp/message3. 改造原服务方法服务的改造有两种思路 - 分别是工具配置方式和函数 Bean 方式,这里对两种方式都做下简略说明: 工具配置方式在需要改造的实现类对需要改造的方法加上 @Tool 和 @ToolParam 注解分别标记方法和参数。
importcom.example.entity.Book;importcom.example.repository.BookRepository;importcom.example.service.BookService;importjakarta.annotation.Resource;importlombok.RequiredArgsConstructor;importorg.springframework.ai.tool.annotation.Tool;importorg.springframework.ai.tool.annotation.ToolParam;importorg.springframework.stereotype.Service;
importjava.util.List;
@Service@RequiredArgsConstructorpublicclassBookServiceImplimplementsBookService{
@ResourceprivateBookRepositorybookRepository;
@Override@Tool(name ="findBooksByTitle", description ="根据书名模糊查询图书,支持部分标题匹配")publicListBookfindBooksByTitle(@ToolParam(description ="书名关键词")Stringtitle) { returnbookRepository.findByTitleContaining(title); }
@Override@Tool(name ="findBooksByAuthor", description ="根据作者精确查询图书")publicListBookfindBooksByAuthor(@ToolParam(description ="作者姓名")Stringauthor) { returnbookRepository.findByAuthor(author); }
@Override@Tool(name ="findBooksByCategory", description ="根据图书分类精确查询图书")publicListBookfindBooksByCategory(@ToolParam(description ="图书分类")Stringcategory) { returnbookRepository.findByCategory(category); }}接着将这个实现类注册到 MCP 服务器配置上即可。
importcom.example.service.BookService;importorg.springframework.ai.tool.ToolCallbackProvider;importorg.springframework.ai.tool.method.MethodToolCallbackProvider;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;
/*** MCP服务器配置类,负责注册MCP工具*/@ConfigurationpublicclassMcpServerConfig{
/** * 注册工具回调提供者,将BookService中的@Tool方法暴露为MCP工具 * *@parambookService 图书服务 *@return工具回调提供者 */@BeanpublicToolCallbackProvider bookToolCallbackProvider(BookService bookService) { returnMethodToolCallbackProvider.builder() .toolObjects(bookService) .build(); }
}此时在聊天客户端配置引入注册工具即可。
importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.tool.ToolCallbackProvider;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;
/*** 聊天客户端配置类*/@ConfigurationpublicclassChatClientConfig{
@AutowiredprivateToolCallbackProvider toolCallbackProvider;
/** * 配置ChatClient,注册系统指令和工具函数 */@BeanpublicChatClient chatClient(ChatClient.Builder builder) { returnbuilder .defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。"+ "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。"+ "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。") // 注册工具方法 .defaultTools(toolCallbackProvider) .build(); }}除了上述的方式,还可以单独声明一个类将查询方法作为函数 Bean 导出。
importcom.example.entity.Book;importcom.example.service.BookService;importjakarta.annotation.Resource;importorg.springframework.context.annotation.Bean;importorg.springframework.stereotype.Service;
importjava.util.List;importjava.util.function.Function;
/*** 图书查询服务,将查询方法作为函数Bean导出*/@ServicepublicclassBookQueryService{
@ResourceprivateBookServicebookService;
/** * 根据书名查询图书的函数Bean */@BeanpublicFunctionString,ListBookfindBooksByTitle() { returntitle - bookService.findBooksByTitle(title); }
/** * 根据作者查询图书的函数Bean */@BeanpublicFunctionString,ListBookfindBooksByAuthor() { returnauthor - bookService.findBooksByAuthor(author); }
/** * 根据分类查询图书的函数Bean */@BeanpublicFunctionString,ListBookfindBooksByCategory() { returncategory - bookService.findBooksByCategory(category); }
}采用这种方式在定义 AI 聊天客户端的时候需要显式地声明。
importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;
/*** 聊天客户端配置类*/@ConfigurationpublicclassChatClientConfig{
/** * 配置ChatClient,注册系统指令和工具函数 */@BeanpublicChatClient chatClient(ChatClient.Builder builder) { returnbuilder .defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。"+ "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。"+ "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。") // 注册工具方法,这里使用方法名称来引用Spring上下文中的函数Bean .defaultTools( "findBooksByTitle", "findBooksByAuthor", "findBooksByCategory" ) .build(); }}4. 接口测试完成了服务开发后,我们就可以声明一个控制器对外暴露进行调用。
importcom.example.model.ChatRequest;importcom.example.model.ChatResponse;importjakarta.annotation.Resource;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.*;
/*** 聊天控制器,处理AI聊天请求*/@RestController@RequestMapping("/api/chat")publicclassChatController{
@ResourceprivateChatClientchatClient;
/** * 处理聊天请求,使用AI和MCP工具进行响应 * *@paramrequest 聊天请求 *@return包含AI回复的响应 */@PostMappingpublicResponseEntityChatResponsechat(@RequestBodyChatRequest request) { try{ // 创建用户消息 StringuserMessage = request.getMessage();
// 使用流式API调用聊天 Stringcontent = chatClient.prompt() .user(userMessage) .call() .content();
returnResponseEntity.ok(newChatResponse(content)); }catch(Exceptione) { e.printStackTrace(); returnResponseEntity.ok(newChatResponse("处理请求时出错: "+ e.getMessage())); } }
}为了方便测试,我们开发一个数据初始化器,通过实现CommandLineRunner接口,它会在我们的应用程序启动时自动向数据库中加载这些测试数据。
importcom.example.entity.Book;importcom.example.repository.BookRepository;importjakarta.annotation.Resource;importlombok.RequiredArgsConstructor;importorg.springframework.boot.CommandLineRunner;importorg.springframework.stereotype.Component;
importjava.time.LocalDate;importjava.util.Arrays;importjava.util.List;
@Component@RequiredArgsConstructorpublicclassDataInitializerimplementsCommandLineRunner{
@ResourceprivateBookRepository bookRepository;
@Overridepublicvoidrun(String... args)throwsException { // 准备示例数据 ListBook sampleBooks = Arrays.asList( newBook(null,"Spring实战(第6版)","编程","Craig Walls", LocalDate.of(2022,1,15),"9787115582247"), newBook(null,"深入理解Java虚拟机","编程","周志明", LocalDate.of(2019,12,1),"9787111641247"), newBook(null,"Java编程思想(第4版)","编程","Bruce Eckel", LocalDate.of(2007,6,1),"9787111213826"), newBook(null,"算法(第4版)","计算机科学","Robert Sedgewick", LocalDate.of(2012,10,1),"9787115293800"), newBook(null,"云原生架构","架构设计","张三", LocalDate.of(2023,3,15),"9781234567890"), newBook(null,"微服务设计模式","架构设计","张三", LocalDate.of(2021,8,20),"9789876543210"), newBook(null,"领域驱动设计","架构设计","Eric Evans", LocalDate.of(2010,4,10),"9787111214748"), newBook(null,"高性能MySQL","数据库","Baron Schwartz", LocalDate.of(2013,5,25),"9787111464747"), newBook(null,"Redis实战","数据库","Josiah L. Carlson", LocalDate.of(2015,9,30),"9787115419378"), newBook(null,"深入浅出Docker","容器技术","李四", LocalDate.of(2022,11,20),"9787123456789")
// 保存示例数据 bookRepository.saveAll(sampleBooks);
System.out.println("数据初始化完成,共加载 "+ sampleBooks.size() +" 本图书"); }
}接下来我们通过请求接口进行如下测试:
可以看到此时返回结果是数据库中的测试数据内容。这里是根据用户输入的问题,大模型会判断我们开放的工具方法中是否有匹配的,如果有则进行调用并返回。
小结通过 Spring Boot 与 MCP 的整合,我们轻松实现了传统 CRUD 系统到智能 AI 助手的转变。MCP 作为 AI 与服务之间的桥梁,极大简化了集成工作。未来随着 MCP 生态发展,"对话即服务" 将可能成为应用的开发范式,让复杂系统变得更加易用。
AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding
点击"阅读原文"了解详情~
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线