对话即服务:Spring Boot整合MCP让你的CRUD系统秒变AI助手

简介: 本文介绍了如何通过Model Context Protocol (MCP) 协议将传统Spring Boot服务改造为支持AI交互的智能系统。MCP作为“万能适配器”,让AI以统一方式与多种服务和数据源交互,降低开发复杂度。文章以图书管理服务为例,详细说明了引入依赖、配置MCP服务器、改造服务方法(注解方式或函数Bean方式)及接口测试的全流程。最终实现用户通过自然语言查询数据库的功能,展示了MCP在简化AI集成、提升系统易用性方面的价值。未来,“对话即服务”有望成为主流开发范式。

引言

随着人工智能的飞速发展,大语言模型(LLM)正在革命性地重塑用户与软件的交互范式。想象一下这样的场景:用户无需钻研复杂的API文档或者在繁琐的表单间来回切换,只需通过自然语言直接与系统对话——"帮我查找所有2023年出版的图书"、"创建一个新用户叫张三,邮箱是zhangsan@example.com"。这种直观、流畅的交互方式不仅能显著降低新用户的学习曲线,更能大幅削减B端系统的培训成本和实施周期,让企业应用变得更为简单和高效。

这正是Model Context Protocol (MCP) 协议在应用层面所带来的价值体现。

认识MCP

我这里不粘贴官方的定义,用大白话给大家解释下:MCP就像是AI世界的"万能适配器"。想象你有很多不同类型的服务和数据库,每个都有自己独特的"说话方式"。AI需要和这些服务交流时就很麻烦,因为要学习每个服务的"语言"。

MCP解决了这个问题 - 它就像一个统一的翻译官,让AI只需学一种"语言"就能和所有服务交流。这样开发者不用为每个服务单独开发连接方式,AI也能更容易获取它需要的信息。

如果你是一个后端同学,那么应该接触或听说过gRPCgRPC通过标准化的通信方式可以实现不同语言开发的服务之间进行通信,那么MCP专门为AI模型设计的"翻译官和接口管理器",让AI能以统一方式与各种应用或数据源交互。

我们假设开发了一个天气服务,用户想要查询深圳的天气,这里分别以传统API方式和MCP方式进行对比:

1.png

对现有Spring Boot服务改造

这里为了演示,先准备好一个图书管理服务,图书实体字段如下:

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PastOrPresent;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


import java.time.LocalDate;

@Entity
@Table(name = "books")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
   

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @NotBlank(message = "书名不能为空")
  @Column(nullable = false)
  private String title;

  @NotBlank(message = "分类不能为空")
  @Column(nullable = false)
  private String category;

  @NotBlank(message = "作者不能为空")
  @Column(nullable = false)
  private String author;

  @NotNull(message = "出版日期不能为空")
  @PastOrPresent(message = "出版日期不能是未来日期")
  @Column(nullable = false)
  private LocalDate publicationDate;

  @NotBlank(message = "ISBN编码不能为空")
  @Column(nullable = false, unique = true)
  private String isbn;

  }
AI 代码解读

为这个服务编写了2个测试方法:

import com.example.entity.Book;

import java.util.List;

public interface BookService {
   

  // 根据作者查询
  List<Book> findBooksByAuthor(String author);

  // 根据分类查询
  List<Book> findBooksByCategory(String category);
}
AI 代码解读

现在我们要将这个SpringBoot服务改造成MCP服务,需要以下步骤:

1.导入依赖

pom.xml中引入相关依赖,这里提示一下anthropic的访问需要代理,否则会提示403。

<!-- Spring AI 核心依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-core</artifactId>
</dependency>

<!-- Anthropic 模型支持 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
</dependency>

<!-- MCP 服务器支持 - WebMVC版本 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>
AI 代码解读

由于目前这些依赖还是预览版本,所以在Maven中央仓库中是找不到的,需要我们额外引入仓库地址。

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
    <repository>
        <name>Central Portal Snapshots</name>
        <id>central-portal-snapshots</id>
        <url>https://central.sonatype.com/repository/maven-snapshots/</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>
AI 代码解读

关于项目中代理的配置可以参考我这段配置:

import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;


@Configuration
public class ProxyConfig {
   

  // 代理设置
  private final String PROXY_HOST = "127.0.0.1";
  private final int PROXY_PORT = 10080;

  @PostConstruct
  public void setSystemProxy() {
   
    // 设置系统代理属性,这会影响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);
  }
}
AI 代码解读

2.引入配置

我们的目的是将一个Spring服务改造成MCP服务,所以这里不需要进行客户端的配置,同理,在引入依赖的时候也不用引入客户端的依赖。

# Spring AI api-key
spring.ai.anthropic.api-key=这里换成你的api-key

# MCP服务端开启
spring.ai.mcp.server.enabled=true

# MCP服务端配置
spring.ai.mcp.server.name=book-management-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.sse-message-endpoint=/mcp/message
AI 代码解读

3.改造原服务方法

服务的改造有两种思路-分别是工具配置方式和函数Bean方式,这里对两种方式都做下简略说明:
工具配置方式在需要改造的实现类对需要改造的方法加上@Tool和@ToolParam注解分别标记方法和参数。

import com.example.entity.Book;
import com.example.repository.BookRepository;
import com.example.service.BookService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class BookServiceImpl  implements BookService {
   

  @Resource
  private BookRepository bookRepository;


  @Override
  @Tool(name = "findBooksByTitle", description = "根据书名模糊查询图书,支持部分标题匹配")
  public List<Book> findBooksByTitle(@ToolParam(description = "书名关键词") String title) {
   
    return bookRepository.findByTitleContaining(title);
  }

  @Override
  @Tool(name = "findBooksByAuthor", description = "根据作者精确查询图书")
  public List<Book> findBooksByAuthor(@ToolParam(description = "作者姓名") String author) {
   
    return bookRepository.findByAuthor(author);
  }

  @Override
  @Tool(name = "findBooksByCategory", description = "根据图书分类精确查询图书")
  public List<Book> findBooksByCategory(@ToolParam(description = "图书分类")String category) {
   
    return bookRepository.findByCategory(category);
  }
}
AI 代码解读

接着将这个实现类注册到MCP服务器配置上即可。

import com.example.service.BookService;
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;

/**
 * MCP服务器配置类,负责注册MCP工具
 */
@Configuration
public class McpServerConfig {
   

  /**
   * 注册工具回调提供者,将BookQueryService中的@Tool方法暴露为MCP工具
   *
   * @param bookService 图书服务
   * @return 工具回调提供者
   */
  @Bean
  public ToolCallbackProvider bookToolCallbackProvider(BookService bookService) {
   
    return MethodToolCallbackProvider.builder()
            .toolObjects(bookService)
            .build();
  }

}
AI 代码解读

此时在聊天客户端配置引入注册工具即可。

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * 聊天客户端配置类
 */
@Configuration
public class ChatClientConfig {
   


  @Autowired
  private ToolCallbackProvider toolCallbackProvider;

  /**
   * 配置ChatClient,注册系统指令和工具函数
   */
  @Bean
  public ChatClient chatClient(ChatClient.Builder builder) {
   
    return builder
            .defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。" +
                    "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
                    "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
            // 注册工具方法
            .defaultTools(toolCallbackProvider)
            .build();
  }
}
AI 代码解读

除了上述的方式,还可以单独声明一个类将查询方法作为函数Bean导出。

import com.example.entity.Book;
import com.example.service.BookService;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.function.Function;

/**
 * 图书查询服务,将查询方法作为函数Bean导出
 */
@Service
public class BookQueryService {
   

  @Resource
  private BookService bookService;

  /**
   * 根据书名查询图书的函数Bean
   */
  @Bean
  public Function<String, List<Book>> findBooksByTitle() {
   
    return title -> bookService.findBooksByTitle(title);
  }

  /**
   * 根据作者查询图书的函数Bean
   */
  @Bean
  public Function<String, List<Book>> findBooksByAuthor() {
   
    return author -> bookService.findBooksByAuthor(author);
  }

  /**
   * 根据分类查询图书的函数Bean
   */
  @Bean
  public Function<String, List<Book>> findBooksByCategory() {
   
    return category -> bookService.findBooksByCategory(category);
  }

}
AI 代码解读

采用这种方式在定义AI聊天客户端的时候需要显式地声明。

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 聊天客户端配置类
 */
@Configuration
public class ChatClientConfig {
   


  /**
   * 配置ChatClient,注册系统指令和工具函数
   */
  @Bean
  public ChatClient chatClient(ChatClient.Builder builder) {
   
    return builder
            .defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。" +
                    "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
                    "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
            // 注册工具方法,这里使用方法名称来引用Spring上下文中的函数Bean
            .defaultTools(
                    "findBooksByTitle",
                    "findBooksByAuthor",
                    "findBooksByCategory"
            )
            .build();
  }
}
AI 代码解读

4.接口测试

完成了服务开发后,我们就可以声明一个控制器对外暴露进行调用。

import com.example.model.ChatRequest;
import com.example.model.ChatResponse;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

/**
 * 聊天控制器,处理AI聊天请求
 */
@RestController
@RequestMapping("/api/chat")
public class ChatController {
   


  @Resource
  private ChatClient chatClient;


  /**
   * 处理聊天请求,使用AI和MCP工具进行响应
   *
   * @param request 聊天请求
   * @return 包含AI回复的响应
   */
  @PostMapping
  public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
   
    try {
   
      // 创建用户消息
      String userMessage = request.getMessage();

      // 使用流式API调用聊天
      String content = chatClient.prompt()
              .user(userMessage)
              .call()
              .content();

      return ResponseEntity.ok(new ChatResponse(content));
    } catch (Exception e) {
   
      e.printStackTrace();
      return ResponseEntity.ok(new ChatResponse("处理请求时出错: " + e.getMessage()));
    }
  }

}
AI 代码解读

为了方便测试,我们开发一个数据初始化器,通过实现CommandLineRunner接口,它会在我们的应用程序启动时自动向数据库中加载这些测试数据。

import com.example.entity.Book;
import com.example.repository.BookRepository;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

@Component
@RequiredArgsConstructor
public class DataInitializer  implements CommandLineRunner {
   

  @Resource
  private BookRepository bookRepository;

  @Override
  public void run(String... args) throws Exception {
   
    // 准备示例数据
    List<Book> sampleBooks = Arrays.asList(
            new Book(null, "Spring实战(第6版)", "编程", "Craig Walls",
                    LocalDate.of(2022, 1, 15), "9787115582247"),
            new Book(null, "深入理解Java虚拟机", "编程", "周志明",
                    LocalDate.of(2019, 12, 1), "9787111641247"),
            new Book(null, "Java编程思想(第4版)", "编程", "Bruce Eckel",
                    LocalDate.of(2007, 6, 1), "9787111213826"),
            new Book(null, "算法(第4版)", "计算机科学", "Robert Sedgewick",
                    LocalDate.of(2012, 10, 1), "9787115293800"),
            new Book(null, "云原生架构", "架构设计", "张三",
                    LocalDate.of(2023, 3, 15), "9781234567890"),
            new Book(null, "微服务设计模式", "架构设计", "张三",
                    LocalDate.of(2021, 8, 20), "9789876543210"),
            new Book(null, "领域驱动设计", "架构设计", "Eric Evans",
                    LocalDate.of(2010, 4, 10), "9787111214748"),
            new Book(null, "高性能MySQL", "数据库", "Baron Schwartz",
                    LocalDate.of(2013, 5, 25), "9787111464747"),
            new Book(null, "Redis实战", "数据库", "Josiah L. Carlson",
                    LocalDate.of(2015, 9, 30), "9787115419378"),
            new Book(null, "深入浅出Docker", "容器技术", "李四",
                    LocalDate.of(2022, 11, 20), "9787123456789")
    );

    // 保存示例数据
    bookRepository.saveAll(sampleBooks);

    System.out.println("数据初始化完成,共加载 " + sampleBooks.size() + " 本图书");
  }

}
AI 代码解读

接下来我们通过请求接口进行如下测试:

2.png

3.png

可以看到此时返回结果是数据库中的测试数据内容。这里是根据用户输入的问题,大模型会判断我们开放的工具方法中是否有匹配的,如果有则进行调用并返回。

小结

通过Spring Boot与MCP的整合,我们轻松实现了传统CRUD系统到智能AI助手的转变。MCP作为AI与服务之间的桥梁,极大简化了集成工作。未来随着MCP生态发展,"对话即服务"将可能成为应用的开发范式,让复杂系统变得更加易用。

目录
打赏
0
1
1
1
152
分享
相关文章
“AI医生”入驻运维现场:聊聊系统健康检查的新姿势
“AI医生”入驻运维现场:聊聊系统健康检查的新姿势
122 78
AI技术如何重塑客服系统?解析合力亿捷AI智能客服系统实践案例
本文探讨了人工智能技术在客服系统中的应用,涵盖技术架构、关键技术和优化策略。通过感知层、认知层、决策层和执行层的协同工作,结合自然语言处理、知识库构建和多模态交互技术,合力亿捷客服系统实现了智能化服务。文章还提出了用户体验优化、服务质量提升和系统性能改进的方法,并展望了未来发展方向,强调其在客户服务领域的核心价值与潜力。
37 6
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
本文介绍了在Spring Boot项目中如何通过创建`GlobalExceptionHandler`类来全局处理系统异常。通过使用`@ControllerAdvice`注解,可以拦截项目中的各种异常,并结合`@ExceptionHandler`注解针对特定异常(如参数缺失、空指针等)进行定制化处理。文中详细展示了处理参数缺失异常和空指针异常的示例代码,并说明了通过拦截`Exception`父类实现统一异常处理的方法。虽然拦截`Exception`可一劳永逸,但为便于问题排查,建议优先处理常见异常,最后再兜底处理未知异常,确保返回给调用方的信息友好且明确。
10 0
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
凌晨急诊室诞生的疫苗系统:一个宝妈的AI破局之路
本文分享了一位妈妈在急诊室经历后,将技术与母爱结合的心路历程。从凌晨抱着高烧儿子就医,同时处理工作告警的崩溃时刻,到意识到妈妈和程序员都是“运维工程师”,作者逐步构建了宝宝疫苗管理系统。文章介绍了系统从静态命令行工具升级为动态智能预警系统的全过程,包括环境搭建、核心代码解析及家庭协同功能实现,并总结了碎片时间开发法与防坑指南。最终,作者通过技术赋予母爱温度,为其他妈妈提供了实用资源包,展现了代码背后的人文关怀。
48 5
Spring AI与DeepSeek实战二:打造企业级智能体
本文介绍如何基于Spring AI与DeepSeek模型构建企业级多语言翻译智能体。通过明确的Prompt设计,该智能体能自主执行复杂任务,如精准翻译32种ISO标准语言,并严格遵循输入格式和行为限制。代码示例展示了如何通过API实现动态Prompt生成和翻译功能,确保服务的安全性和可控性。项目已开源,提供更多细节和完整代码。 [GitHub](https://github.com/zlt2000/zlt-spring-ai-app) | [Gitee](https://gitee.com/zlt2000/zlt-spring-ai-app)
127 11
【最佳实践系列】AI程序员让我变成全栈:基于阿里云百炼DeepSeek的跨语言公告系统实战
本文介绍了如何在Java开发中通过跨语言编程,利用阿里云百炼服务平台的DeepSeek大模型生成公告内容,并将其嵌入前端页面。
|
19天前
|
Spring AI与DeepSeek实战一:快速打造智能对话应用
在 AI 技术蓬勃发展的今天,国产大模型DeepSeek凭借其低成本高性能的特点,成为企业智能化转型的热门选择。而Spring AI作为 Java 生态的 AI 集成框架,通过统一API、简化配置等特性,让开发者无需深入底层即可快速调用各类 AI 服务。本文将手把手教你通过spring-ai集成DeepSeek接口实现普通对话与流式对话功能,助力你的Java应用轻松接入 AI 能力!虽然通过Spring AI能够快速完成DeepSeek大模型与。
342 11
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
Java 也能快速搭建 AI 应用?一文带你玩转 Spring AI 可观测性
从零开始即刻拥有 DeepSeek-R1 满血版并使用 Dify 部署 AI 应用
本文介绍了如何使用阿里云提供的DeepSeek-R1大模型解决方案,通过Chatbox和Dify平台调用百炼API,实现稳定且高效的模型应用。首先,文章详细描述了如何通过Chatbox配置API并开始对话,适合普通用户快速上手。接着,深入探讨了使用Dify部署AI应用的过程,包括选购云服务器、安装Dify、配置对接DeepSeek-R1模型及创建工作流,展示了更复杂场景下的应用潜力。最后,对比了Chatbox与Dify的输出效果,证明Dify能提供更详尽、精准的回复。总结指出,阿里云的解决方案不仅操作简便,还为专业用户提供了强大的功能支持,极大提升了用户体验和应用效率。
986 19
从零开始即刻拥有 DeepSeek-R1 满血版并使用 Dify 部署 AI 应用