Java精選面試題(微信小程序):5000+道面試題和選擇題,真實(shí)面經(jīng),簡(jiǎn)歷模版,包含Java基礎(chǔ)、并發(fā)、JVM、線程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架構(gòu)設(shè)計(jì)、大廠真題等,在線隨時(shí)刷題!

引言

想象一下這樣的場(chǎng)景:用戶無(wú)需鉆研復(fù)雜的API文檔或者在繁瑣的表單間來(lái)回切換,只需通過(guò)自然語(yǔ)言直接與系統(tǒng)對(duì)話——“幫我查找所有2023年出版的圖書(shū)”、“創(chuàng)建一個(gè)新用戶叫張三,郵箱是zhangsan@example.com”。

這種直觀、流暢的交互方式不僅能顯著降低新用戶的學(xué)習(xí)曲線,更能大幅削減B端系統(tǒng)的培訓(xùn)成本和實(shí)施周期,讓企業(yè)應(yīng)用變得更為簡(jiǎn)單和高效。

這正是Model Context Protocol (MCP)協(xié)議在應(yīng)用層面所帶來(lái)的價(jià)值體現(xiàn)。

認(rèn)識(shí)MCP

我這里不粘貼官方的定義,用大白話給大家解釋下:MCP就像是AI世界的"萬(wàn)能適配器"。想象你有很多不同類(lèi)型的服務(wù)和數(shù)據(jù)庫(kù),每個(gè)都有自己獨(dú)特的"說(shuō)話方式"。AI需要和這些服務(wù)交流時(shí)就很麻煩,因?yàn)橐獙W(xué)習(xí)每個(gè)服務(wù)的"語(yǔ)言"。

MCP解決了這個(gè)問(wèn)題 - 它就像一個(gè)統(tǒng)一的翻譯官,讓AI只需學(xué)一種"語(yǔ)言"就能和所有服務(wù)交流。這樣開(kāi)發(fā)者不用為每個(gè)服務(wù)單獨(dú)開(kāi)發(fā)連接方式,AI也能更容易獲取它需要的信息。

如果你是一個(gè)后端同學(xué),那么應(yīng)該接觸或聽(tīng)說(shuō)過(guò)gRPC。gRPC通過(guò)標(biāo)準(zhǔn)化的通信方式可以實(shí)現(xiàn)不同語(yǔ)言開(kāi)發(fā)的服務(wù)之間進(jìn)行通信,那么MCP專(zhuān)門(mén)為AI模型設(shè)計(jì)的"翻譯官和接口管理器",讓AI能以統(tǒng)一方式與各種應(yīng)用或數(shù)據(jù)源交互。

我們假設(shè)開(kāi)發(fā)了一個(gè)天氣服務(wù),用戶想要查詢深圳的天氣,這里分別以傳統(tǒng)API方式和MCP方式進(jìn)行對(duì)比:

對(duì)現(xiàn)有SpringBoot服務(wù)改造
打開(kāi)網(wǎng)易新聞 查看精彩圖片
對(duì)現(xiàn)有SpringBoot服務(wù)改造

這里為了演示,先準(zhǔn)備好一個(gè)圖書(shū)管理服務(wù),圖書(shū)實(shí)體字段如下:

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 = "書(shū)名不能為空") @Column(nullable = false) private String title; @NotBlank(message = "分類(lèi)不能為空") @Column(nullable = false) private String category; @NotBlank(message = "作者不能為空") @Column(nullable = false) private String author; @NotNull(message = "出版日期不能為空") @PastOrPresent(message = "出版日期不能是未來(lái)日期") @Column(nullable = false) private LocalDate publicationDate; @NotBlank(message = "ISBN編碼不能為空") @Column(nullable = false, unique = true) private String isbn;   }

為這個(gè)服務(wù)編寫(xiě)了2個(gè)測(cè)試方法:

import com.example.entity.Book; import java.util.List; publicinterfaceBookService{ // 根據(jù)作者查詢 List   findBooksByAuthor(String author); // 根據(jù)分類(lèi)查詢 List   findBooksByCategory(String category); }

現(xiàn)在我們要將這個(gè)SpringBoot服務(wù)改造成MCP服務(wù),需要以下步驟:

1.導(dǎo)入依賴(lài)

在pom.xml中引入相關(guān)依賴(lài),這里提示一下anthropic的訪問(wèn)需要代理,否則會(huì)提示403。


     

 org.springframework.ai groupId>     

 spring-ai-core artifactId> dependency> 

     

 org.springframework.ai groupId>     

 spring-ai-anthropic-spring-boot-starter artifactId> dependency> 

     

 org.springframework.ai groupId>     

 spring-ai-mcp-server-webmvc-spring-boot-starter artifactId> dependency>








由于目前這些依賴(lài)還是預(yù)覽版本,所以在Maven中央倉(cāng)庫(kù)中是找不到的,需要我們額外引入倉(cāng)庫(kù)地址。

推薦程序員摸魚(yú)地址:

https://www.yoodb.com/slack-off/home.html


     

         

 spring-milestones id>         

 Spring Milestones name>         

 https://repo.spring.io/milestone url>         

             

 false enabled>          snapshots>      repository>     

         

 spring-snapshots id>         

 Spring Snapshots name>         

 https://repo.spring.io/snapshot url>         

             

 false enabled>          releases>      repository>     

         

 Central Portal Snapshots name>         

 central-portal-snapshots id>         

 https://central.sonatype.com/repository/maven-snapshots/ url>         

             

 false enabled>          releases>         

             

 true enabled>          snapshots>      repository> repositories>




















關(guān)于項(xiàng)目中代理的配置可以參考我這段配置:

import jakarta.annotation.PostConstruct; import org.springframework.context.annotation.Configuration; @Configuration publicclassProxyConfig{ // 代理設(shè)置 privatefinal String PROXY_HOST = "127.0.0.1"; privatefinalint PROXY_PORT = 10080; @PostConstruct publicvoidsetSystemProxy(){     // 設(shè)置系統(tǒng)代理屬性,這會(huì)影響Spring Boot自動(dòng)配置的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.引入配置

我們的目的是將一個(gè)Spring服務(wù)改造成MCP服務(wù),所以這里不需要進(jìn)行客戶端的配置,同理,在引入依賴(lài)的時(shí)候也不用引入客戶端的依賴(lài)。

# Spring AI api-key spring.ai.anthropic.api-key=這里換成你的api-key # MCP服務(wù)端開(kāi)啟 spring.ai.mcp.server.enabled=true # MCP服務(wù)端配置 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.改造原服務(wù)方法

服務(wù)的改造有兩種思路-分別是工具配置方式和函數(shù)Bean方式,這里對(duì)兩種方式都做下簡(jiǎn)略說(shuō)明:

工具配置方式在需要改造的實(shí)現(xiàn)類(lèi)對(duì)需要改造的方法加上@Tool@ToolParam注解分別標(biāo)記方法和參數(shù)。

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 = "根據(jù)書(shū)名模糊查詢圖書(shū),支持部分標(biāo)題匹配") public List   findBooksByTitle(@ToolParam(description = "書(shū)名關(guān)鍵詞") String title) {     return bookRepository.findByTitleContaining(title);   } @Override @Tool(name = "findBooksByAuthor", description = "根據(jù)作者精確查詢圖書(shū)") public List   findBooksByAuthor(@ToolParam(description = "作者姓名") String author) {     return bookRepository.findByAuthor(author);   } @Override @Tool(name = "findBooksByCategory", description = "根據(jù)圖書(shū)分類(lèi)精確查詢圖書(shū)") public List   findBooksByCategory(@ToolParam(description = "圖書(shū)分類(lèi)")String category) {     return bookRepository.findByCategory(category);   } }

接著將這個(gè)實(shí)現(xiàn)類(lèi)注冊(cè)到MCP服務(wù)器配置上即可。

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服務(wù)器配置類(lèi),負(fù)責(zé)注冊(cè)MCP工具  */ @Configuration publicclassMcpServerConfig{ /**    * 注冊(cè)工具回調(diào)提供者,將BookQueryService中的@Tool方法暴露為MCP工具    *    * @param bookService 圖書(shū)服務(wù)    * @return 工具回調(diào)提供者    */ @Bean public ToolCallbackProvider bookToolCallbackProvider(BookService bookService){     return MethodToolCallbackProvider.builder()             .toolObjects(bookService)             .build();   } }

此時(shí)在聊天客戶端配置引入注冊(cè)工具即可。

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; /**  * 聊天客戶端配置類(lèi)  */ @Configuration publicclassChatClientConfig{ @Autowired private ToolCallbackProvider toolCallbackProvider; /**    * 配置ChatClient,注冊(cè)系統(tǒng)指令和工具函數(shù)    */ @Bean public ChatClient chatClient(ChatClient.Builder builder){     return builder             .defaultSystem("你是一個(gè)圖書(shū)管理助手,可以幫助用戶查詢圖書(shū)信息。" +                     "你可以根據(jù)書(shū)名模糊查詢、根據(jù)作者查詢和根據(jù)分類(lèi)查詢圖書(shū)。" +                     "回復(fù)時(shí),請(qǐng)使用簡(jiǎn)潔友好的語(yǔ)言,并將圖書(shū)信息整理為易讀的格式。")             // 注冊(cè)工具方法             .defaultTools(toolCallbackProvider)             .build();   } }

除了上述的方式,還可以單獨(dú)聲明一個(gè)類(lèi)將查詢方法作為函數(shù)Bean導(dǎo)出。

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; /**  * 圖書(shū)查詢服務(wù),將查詢方法作為函數(shù)Bean導(dǎo)出  */ @Service publicclassBookQueryService{ @Resource private BookService bookService; /**    * 根據(jù)書(shū)名查詢圖書(shū)的函數(shù)Bean    */ @Bean public Function > findBooksByTitle() {     return title -> bookService.findBooksByTitle(title);   } /**    * 根據(jù)作者查詢圖書(shū)的函數(shù)Bean    */ @Bean public Function > findBooksByAuthor() {     return author -> bookService.findBooksByAuthor(author);   } /**    * 根據(jù)分類(lèi)查詢圖書(shū)的函數(shù)Bean    */ @Bean public Function > findBooksByCategory() {     return category -> bookService.findBooksByCategory(category);   } }

采用這種方式在定義AI聊天客戶端的時(shí)候需要顯式地聲明。

import org.springframework.ai.chat.client.ChatClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /**  * 聊天客戶端配置類(lèi)  */ @Configuration publicclassChatClientConfig{ /**    * 配置ChatClient,注冊(cè)系統(tǒng)指令和工具函數(shù)    */ @Bean public ChatClient chatClient(ChatClient.Builder builder){     return builder             .defaultSystem("你是一個(gè)圖書(shū)管理助手,可以幫助用戶查詢圖書(shū)信息。" +                     "你可以根據(jù)書(shū)名模糊查詢、根據(jù)作者查詢和根據(jù)分類(lèi)查詢圖書(shū)。" +                     "回復(fù)時(shí),請(qǐng)使用簡(jiǎn)潔友好的語(yǔ)言,并將圖書(shū)信息整理為易讀的格式。")             // 注冊(cè)工具方法,這里使用方法名稱(chēng)來(lái)引用Spring上下文中的函數(shù)Bean             .defaultTools(                     "findBooksByTitle",                     "findBooksByAuthor",                     "findBooksByCategory"             )             .build();   } }
4.接口測(cè)試

完成了服務(wù)開(kāi)發(fā)后,我們就可以聲明一個(gè)控制器對(duì)外暴露進(jìn)行調(diào)用。

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聊天請(qǐng)求  */ @RestController @RequestMapping("/api/chat") publicclassChatController{ @Resource private ChatClient chatClient; /**    * 處理聊天請(qǐng)求,使用AI和MCP工具進(jìn)行響應(yīng)    *    * @param request 聊天請(qǐng)求    * @return 包含AI回復(fù)的響應(yīng)    */ @PostMapping public ResponseEntity   chat(@RequestBody ChatRequest request){     try {       // 創(chuàng)建用戶消息       String userMessage = request.getMessage();       // 使用流式API調(diào)用聊天       String content = chatClient.prompt()               .user(userMessage)               .call()               .content();       return ResponseEntity.ok(new ChatResponse(content));     } catch (Exception e) {       e.printStackTrace();       return ResponseEntity.ok(new ChatResponse("處理請(qǐng)求時(shí)出錯(cuò): " + e.getMessage()));     }   } }

為了方便測(cè)試,我們開(kāi)發(fā)一個(gè)數(shù)據(jù)初始化器,通過(guò)實(shí)現(xiàn)CommandLineRunner接口,它會(huì)在我們的應(yīng)用程序啟動(dòng)時(shí)自動(dòng)向數(shù)據(jù)庫(kù)中加載這些測(cè)試數(shù)據(jù)。

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 {     // 準(zhǔn)備示例數(shù)據(jù)     List sampleBooks = Arrays.asList(             new Book(null, "Spring實(shí)戰(zhàn)(第6版)", "編程", "Craig Walls",                     LocalDate.of(2022, 1, 15), "9787115582247"),             new Book(null, "深入理解Java虛擬機(jī)", "編程", "周志明",                     LocalDate.of(2019, 12, 1), "9787111641247"),             new Book(null, "Java編程思想(第4版)", "編程", "Bruce Eckel",                     LocalDate.of(2007, 6, 1), "9787111213826"),             new Book(null, "算法(第4版)", "計(jì)算機(jī)科學(xué)", "Robert Sedgewick",                     LocalDate.of(2012, 10, 1), "9787115293800"),             new Book(null, "云原生架構(gòu)", "架構(gòu)設(shè)計(jì)", "張三",                     LocalDate.of(2023, 3, 15), "9781234567890"),             new Book(null, "微服務(wù)設(shè)計(jì)模式", "架構(gòu)設(shè)計(jì)", "張三",                     LocalDate.of(2021, 8, 20), "9789876543210"),             new Book(null, "領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)", "架構(gòu)設(shè)計(jì)", "Eric Evans",                     LocalDate.of(2010, 4, 10), "9787111214748"),             new Book(null, "高性能MySQL", "數(shù)據(jù)庫(kù)", "Baron Schwartz",                     LocalDate.of(2013, 5, 25), "9787111464747"),             new Book(null, "Redis實(shí)戰(zhàn)", "數(shù)據(jù)庫(kù)", "Josiah L. Carlson",                     LocalDate.of(2015, 9, 30), "9787115419378"),             new Book(null, "深入淺出Docker", "容器技術(shù)", "李四",                     LocalDate.of(2022, 11, 20), "9787123456789")     );     // 保存示例數(shù)據(jù)     bookRepository.saveAll(sampleBooks);     System.out.println("數(shù)據(jù)初始化完成,共加載 " + sampleBooks.size() + " 本圖書(shū)");   } }

接下來(lái)我們通過(guò)請(qǐng)求接口進(jìn)行如下測(cè)試:

打開(kāi)網(wǎng)易新聞 查看精彩圖片

可以看到此時(shí)返回結(jié)果是數(shù)據(jù)庫(kù)中的測(cè)試數(shù)據(jù)內(nèi)容。這里是根據(jù)用戶輸入的問(wèn)題,大模型會(huì)判斷我們開(kāi)放的工具方法中是否有匹配的,如果有則進(jìn)行調(diào)用并返回。

小結(jié)

通過(guò)Spring Boot與MCP的整合,我們輕松實(shí)現(xiàn)了傳統(tǒng)CRUD系統(tǒng)到智能AI助手的轉(zhuǎn)變。MCP作為AI與服務(wù)之間的橋梁,極大簡(jiǎn)化了集成工作。未來(lái)隨著MCP生態(tài)發(fā)展,"對(duì)話即服務(wù)"將可能成為應(yīng)用的開(kāi)發(fā)范式,讓復(fù)雜系統(tǒng)變得更加易用。

代碼示例:

https://github.com/Pitayafruits/spring-boot-mcp-demo

來(lái)源:https://juejin.cn/post/7483454392979570700

公眾號(hào)“Java精選”所發(fā)表內(nèi)容注明來(lái)源的,版權(quán)歸原出處所有(無(wú)法查證版權(quán)的或者未注明出處的均來(lái)自網(wǎng)絡(luò),系轉(zhuǎn)載,轉(zhuǎn)載的目的在于傳遞更多信息,版權(quán)屬于原作者。如有侵權(quán),請(qǐng)聯(lián)系,筆者會(huì)第一時(shí)間刪除處理!

最近有很多人問(wèn),有沒(méi)有讀者或者摸魚(yú)交流群!加入方式很簡(jiǎn)單,公眾號(hào)Java精選,回復(fù)“加群”,即可入群!

文章有幫助的話,點(diǎn)在看,轉(zhuǎn)發(fā)吧!