admin管理员组文章数量:1439726
牛逼!实操 SpringBoot+MCP!
大家好,欢迎来到程序视点
!我是你们的老朋友.小二!需要IDEA、PyCharm等全家桶激活的小伙伴,可以关注微信公众号【程序视点】,回复【vip】,获取激活优惠!
引言
随着人工智能的飞速发展,大语言模型(LLM)正在革命性地重塑用户与软件的交互范式。
想象一下这样的场景:用户无需钻研复杂的API文档或者在繁琐的表单间来回切换,只需通过自然语言直接与系统对话——“帮我查找所有2023年出版的图书
”、“创建一个新用户叫张三,邮箱是zhangsan@example
”。
这种直观、流畅的交互方式不仅能显著降低新用户的学习曲线,更能大幅削减B端系统的培训成本和实施周期,让企业应用变得更为简单和高效。
这正是Model Context Protocol (MCP)
协议在应用层面所带来的价值体现。
认识MCP
用大白话给大家解释下:MCP就像是AI世界的"万能适配器"。想象你有很多不同类型的服务和数据库,每个都有自己独特的"说话方式"。AI需要和这些服务交流时就很麻烦,因为要学习每个服务的"语言"。
MCP解决了这个问题 - 它就像一个统一的翻译官,让AI只需学一种"语言"就能和所有服务交流。这样开发者不用为每个服务单独开发连接方式,AI也能更容易获取它需要的信息。
如果你是一个后端同学,那么应该接触或听说过gRPC。gRPC通过标准化的通信方式可以实现不同语言开发的服务之间进行通信,那么MCP专门为AI模型设计的"翻译官和接口管理器",让AI能以统一方式与各种应用或数据源交互。
我们假设开发了一个天气服务,用户想要查询深圳的天气,这里分别以传统API方式和MCP方式进行对比:
对现有SpringBoot服务改造
这里为了演示,先准备好一个图书管理服务,图书实体字段如下:
代码语言:javascript代码运行次数:0运行复制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
publicclassBook{
@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;
}
为这个服务编写了2个测试方法:
代码语言:javascript代码运行次数:0运行复制import com.example.entity.Book;
import java.util.List;
publicinterfaceBookService{
// 根据作者查询
List<Book> findBooksByAuthor(String author);
// 根据分类查询
List<Book> findBooksByCategory(String category);
}
现在我们要将这个SpringBoot服务改造成MCP服务,需要以下步骤:
1.导入依赖
在pom.xml中引入相关依赖,这里提示一下anthropic的访问需要代理,否则会提示403。
代码语言:javascript代码运行次数:0运行复制<!-- 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>
由于目前这些依赖还是预览版本,所以在Maven中央仓库中是找不到的,需要我们额外引入仓库地址。
代码语言:javascript代码运行次数:0运行复制<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>;/url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>;/url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
关于项目中代理的配置可以参考我这段配置:
代码语言:javascript代码运行次数:0运行复制import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;
@Configuration
publicclassProxyConfig{
// 代理设置
privatefinal String PROXY_HOST = "127.0.0.1";
privatefinalint PROXY_PORT = ;
@PostConstruct
publicvoidsetSystemProxy(){
// 设置系统代理属性,这会影响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服务,所以这里不需要进行客户端的配置,同理,在引入依赖的时候也不用引入客户端的依赖。
代码语言:javascript代码运行次数:0运行复制# 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
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
publicclassBookServiceImpl implementsBookService{
@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);
}
}
接着将这个实现类注册到MCP服务器配置上即可。
代码语言:javascript代码运行次数:0运行复制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
publicclassMcpServerConfig{
/**
* 注册工具回调提供者,将BookQueryService中的@Tool方法暴露为MCP工具
*
* @param bookService 图书服务
* @return 工具回调提供者
*/
@Bean
public ToolCallbackProvider bookToolCallbackProvider(BookService bookService){
return MethodToolCallbackProvider.builder()
.toolObjects(bookService)
.build();
}
}
此时在聊天客户端配置引入注册工具即可。
代码语言:javascript代码运行次数:0运行复制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
publicclassChatClientConfig{
@Autowired
private ToolCallbackProvider toolCallbackProvider;
/**
* 配置ChatClient,注册系统指令和工具函数
*/
@Bean
public ChatClient chatClient(ChatClient.Builder builder){
return builder
.defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。" +
"你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
"回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
// 注册工具方法
.defaultTools(toolCallbackProvider)
.build();
}
}
除了上述的方式,还可以单独声明一个类将查询方法作为函数Bean导出。
代码语言:javascript代码运行次数:0运行复制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
publicclassBookQueryService{
@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聊天客户端的时候需要显式地声明。
代码语言:javascript代码运行次数:0运行复制import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 聊天客户端配置类
*/
@Configuration
publicclassChatClientConfig{
/**
* 配置ChatClient,注册系统指令和工具函数
*/
@Bean
public ChatClient chatClient(ChatClient.Builder builder){
return builder
.defaultSystem("你是一个图书管理助手,可以帮助用户查询图书信息。" +
"你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
"回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
// 注册工具方法,这里使用方法名称来引用Spring上下文中的函数Bean
.defaultTools(
"findBooksByTitle",
"findBooksByAuthor",
"findBooksByCategory"
)
.build();
}
}
4.接口测试
完成了服务开发后,我们就可以声明一个控制器对外暴露进行调用。
代码语言:javascript代码运行次数:0运行复制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")
publicclassChatController{
@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()));
}
}
}
为了方便测试,我们开发一个数据初始化器,通过实现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
publicclassDataInitializer implementsCommandLineRunner{
@Resource
private BookRepository bookRepository;
@Override
publicvoidrun(String... args)throws Exception {
// 准备示例数据
List<Book> sampleBooks = Arrays.asList(
new Book(null, "Spring实战(第6版)", "编程", "Craig Walls",
LocalDate.of(, , ), "9787115582247"),
new Book(null, "深入理解Java虚拟机", "编程", "周志明",
LocalDate.of(, , ), "9787111641247"),
new Book(null, "Java编程思想(第4版)", "编程", "Bruce Eckel",
LocalDate.of(, , ), "9787111213826"),
new Book(null, "算法(第4版)", "计算机科学", "Robert Sedgewick",
LocalDate.of(, , ), "9787115293800"),
new Book(null, "云原生架构", "架构设计", "张三",
LocalDate.of(, , ), "9781234567890"),
new Book(null, "微服务设计模式", "架构设计", "张三",
LocalDate.of(, , ), "9789876543210"),
new Book(null, "领域驱动设计", "架构设计", "Eric Evans",
LocalDate.of(, , ), "9787111214748"),
new Book(null, "高性能MySQL", "数据库", "Baron Schwartz",
LocalDate.of(, , ), "9787111464747"),
new Book(null, "Redis实战", "数据库", "Josiah L. Carlson",
LocalDate.of(, , ), "9787115419378"),
new Book(null, "深入浅出Docker", "容器技术", "李四",
LocalDate.of(, , ), "9787123456789")
);
// 保存示例数据
bookRepository.saveAll(sampleBooks);
System.out.println("数据初始化完成,共加载 " + sampleBooks.size() + " 本图书");
}
}
接下来我们通过请求接口进行如下测试:
可以看到此时返回结果是数据库中的测试数据内容。这里是根据用户输入的问题,大模型会判断我们开放的工具方法中是否有匹配的,如果有则进行调用并返回。
小结
通过Spring Boot与MCP的整合,我们轻松实现了传统CRUD系统到智能AI助手的转变。MCP作为AI与服务之间的桥梁,极大简化了集成工作。未来随着MCP生态发展,"对话即服务"将可能成为应用的开发范式,让复杂系统变得更加易用。
代码示例:
来源:juejin/post/7483454392979570700
代码语言:javascript代码运行次数:0运行复制End
专属付费版全家桶
激活JetBrains全家桶IDE的小伙伴,可以选择目前最经济、最实惠、最有保障的方案了!
专属付费版全家桶
除了支持IDE的正常激活外,还支持常用的付费插件和付费主题
!
100%保障激活,100%稳定使用,100%售后兜底!
为什么说专属付费版全家桶最经济、最实惠?
因为专属付费版全家桶
支持常用付费插件和付费主题
。而任意一款或两款付费插件或付费主题,其激活费用就远高于我提供的专属付费版全家桶
。
比如,最方便的彩虹括号符Rainbow Brackets,124/年。
再如,MyBatis最佳辅助框架MyBatisCodeHelperPro (Marketplace Edition),157/年。
还有最牛的Fast Request
,集API调试工具 + API管理工具 + API搜索工具一体!157/年
。
本文标签: 牛逼!实操 SpringBootMCP!
版权声明:本文标题:牛逼!实操 SpringBoot+MCP! 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1747672374a2741390.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论