Java设计模式在MCP Server设计实战中的应用
本文主要探讨关于 Java设计模式在MCP Server设计中的应用。MCP协议作为AI模型与外部系统的通信标准,其实现需要稳健的架构设计。文章首先解析了MCP协议的基础概念、核心能力和在AIGC生态系统中的定位,然后阐述了如何将设计原则重构以适应AI应用需求。重点介绍了创建型模式(工厂方法、单例、建造者)在资源管理中的应用,以及结构型模式(适配器、组合)在系统集成中的实践。通过设计模式的合理运用
Java设计模式在MCP Server设计实战中的应用
一、引言
随着AIGC技术的快速发展,如何高效、安全地集成AI模型与外部系统和数据源成为关键挑战。Model Context Protocol正是为解决这一问题而生的开放标准,其核心价值在于为AI模型与外部工具、资源之间的通信提供了统一规范,从而构建更加灵活和可扩展的AI应用生态。在这一背景下,Java凭借其稳健的类型系统、成熟的工程实践以及丰富的设计模式体系,成为构建高可靠MCP应用的理想选择。设计模式作为软件设计的经验结晶,通过提供可复用的解决方案,帮助开发者在MCP应用中构建更加清晰、可维护和可扩展的架构。本文将深入探讨如何将Java设计模式与MCP设计相结合,通过模式组合解决复杂集成问题,为AIGC应用提供坚实的技术基础。
二、MCP协议深入解析
1. MCP基础概念与架构
Model Context Protocol是Anthropic于2024年推出的开放标准,本质上是一个专属的RPC协议,旨在统一AI模型与外部数据源、工具之间的通信方式。其核心架构基于客户端-服务器模型,包含三个关键角色:Host负责管理AI模型和对话流,Client提供工具和资源,Server则作为MCP协议的实现端,向外暴露能力。
MCP协议通过两种传输方式实现通信:STDIO模式适用于本地进程间通信,通过标准输入输出进行数据交换;SSE模式则基于HTTP服务器推送技术,更适合远程和网络环境下的交互。这种双模式设计使MCP既能满足本地部署的低延迟需求,也能适应分布式场景的灵活性要求。
2. MCP核心能力与消息机制
MCP协议定义了三种核心能力,构成了AI与外部系统交互的基础:
- 工具:AI模型可调用的操作与方法,例如查询天气、执行计算等。在Solon-MCP中,工具通过
@ToolMapping
注解标识,并支持详细的参数描述。 - 资源:只读数据访问接口,提供结构化数据的读取能力。通过
@ResourceMapping
注解定义,支持JSON等标准格式。 - 提示模板:可复用的对话模板,帮助标准化AI模型的交互模式。
MCP的消息机制基于JSON-RPC 2.0协议,支持请求-响应和发布-订阅两种模式。生命周期管理包括初始化、协商和通知三个关键阶段,确保客户端和服务器能够正确建立连接并交换能力信息。
3. MCP在AIGC生态系统中的位置
在AIGC生态系统中,MCP扮演着"AI系统的USB-C接口"角色,解决了传统API设计与AI模型使用模式之间的不匹配问题。与传统的REST或GraphQL API相比,MCP更加语义化,其工具发现和使用机制更适合AI模型的动态调用特性。
目前,主流框架如LangChain4j、Solon AI等均已提供对MCP的原生支持。在企业级部署中,MCP应用需要考虑安全控制、性能优化和可扩展性等方面,确保其能够满足生产环境的要求。
三、设计模式在MCP应用中的基础框架
1. 面向AI应用的设计原则重构
构建稳健的MCP应用需要重新审视和调整经典的设计原则,以适应AI集成的特殊需求:
- 开闭原则在MCP语境下体现为工具的可扩展性设计,应保证能够在不修改现有代码的情况下增加新的工具和能力。
- 依赖倒置原则要求高层模块不依赖于低层模块,二者都应依赖于抽象。在MCP应用中,这意味着AI模型不应直接依赖具体的工具实现,而应通过抽象接口进行交互。
- 接口隔离原则建议将庞大的接口拆分为更小、更具体的接口,使得客户端只需知道它们感兴趣的方法。这对于MCP工具设计尤为重要,可以最小化AI客户端需要理解的接口复杂度。
2. MCP应用的分层架构模型
一个典型的MCP应用可以采用分层架构,每层都有明确的职责和设计模式应用点:
- 传输层:负责协议适配与通信管理,处理STDIO或SSE传输细节。在此层,适配器模式和工厂模式可以封装不同传输协议的差异。
- 服务层:包含业务逻辑与工具实现,是MCP应用的核心。策略模式和模板方法模式可以在这里规范工具的执行流程。
- 集成层:处理AI模型与外部系统的连接,代理模式和外观模式在这里提供额外的控制层和简化接口。
四、创建型模式在MCP资源管理中的应用
1. 工厂方法模式:MCP工具动态实例化
工厂方法模式在MCP应用中用于按需创建工具实例,特别适合于需要根据运行时条件动态选择工具实现的场景。通过将工具实例化逻辑封装在工厂类中,可以实现工具创建与使用的解耦。
在Solon-MCP框架中,工厂方法模式可以与MCP工具发现机制自然结合。当MCP客户端请求可用工具列表时,工厂可以根据系统状态、用户权限或其他因素决定提供哪些工具实例。
// 工具工厂示例
public abstract class ToolFactory {
public abstract McpTool createTool();
// 其他公共逻辑...
}
public class WeatherToolFactory extends ToolFactory {
@Override
public McpTool createTool() {
return new WeatherTool();
}
}
2. 单例模式:资源共享与状态管理
单例模式确保一个类只有一个实例,并提供一个全局访问点。在MCP应用中,单例模式适用于管理共享资源,如数据库连接池、缓存管理器或配置信息。
需要注意的是,在分布式MCP部署环境中,传统的单例模式可能需要调整为集群感知的单例,或者通过外部存储(如Redis)来维护全局状态。
// 资源管理器的单例实现
public class ResourceManager {
private static volatile ResourceManager instance;
private ResourceManager() {
// 初始化资源
}
public static ResourceManager getInstance() {
if (instance == null) {
synchronized (ResourceManager.class) {
if (instance == null) {
instance = new ResourceManager();
}
}
}
return instance;
}
}
3. 建造者模式:复杂MCP消息构建
建造者模式通过一步一步的方式构建复杂对象,特别适用于具有多个可选参数或配置项的MCP消息和资源描述对象。在MCP协议中,工具调用可能包含复杂的参数结构,建造者模式可以提供更加清晰和易用的构建方式。
// 使用建造者模式构建复杂的MCP请求
McpRequest request = new McpRequest.Builder()
.method("tools/call")
.params(new ToolCallParams.Builder()
.name("weather_forecast")
.arguments("{'city': 'Beijing', 'days': 5}")
.build())
.build();
五、结构型模式在MCP系统集成中的应用
1. 适配器模式:遗留系统MCP化改造
适配器模式通过将一个类的接口转换成客户端期望的另一个接口,使原本接口不兼容的类可以一起工作。在MCP应用中,适配器模式是集成现有系统和遗留代码的关键技术。
例如,可以将传统的REST API通过适配器模式暴露为MCP工具,使AI模型能够无缝使用现有系统功能:
// 将传统REST服务适配为MCP工具
@McpServerEndpoint(name = "legacy-adapter")
public class LegacySystemAdapter {
@ToolMapping(description = "查询用户信息")
public String getUserInfo(@Param String userId) {
// 调用传统REST API并适配响应格式
RestResponse response = legacyUserService.getUser(userId);
return adaptToMcpFormat(response);
}
}
2. 组合模式:层级化MCP工具组织
组合模式使用树形结构来表示"部分-整体"的层次关系,使得用户对单个对象和组合对象的使用具有一致性。在MCP应用中,组合模式可以用于管理具有层次关系的工具集。
例如,一个文件系统MCP工具可以使用组合模式来表示文件和目录结构,使AI模型能够以统一的方式操作单个文件和整个目录:
// 组合模式的抽象构件
public abstract class FileSystemResource {
protected String name;
public FileSystemResource(String name) {
this.name = name;
}
public abstract List<String> listContents();
public abstract String getInfo();
}
// 叶子节点 - 文件
public class File extends FileSystemResource {
@Override
public List<String> listContents() {
return Collections.singletonList(this.name);
}
@Override
public String getInfo() {
return "File: " + name;
}
}
// 容器节点 - 目录
public class Directory extends FileSystemResource {
private List<FileSystemResource> children = new ArrayList<>();
@Override
public List<String> listContents() {
List<String> contents = new ArrayList<>();
for (FileSystemResource child : children) {
contents.addAll(child.listContents());
}
return contents;
}
public void addChild(FileSystemResource child) {
children.add(child);
}
}
3. 外观模式:复杂子系统简化接口
外观模式为子系统中的一组接口提供了一个统一的高层接口,使得子系统更容易使用。在MCP应用中,外观模式可以简化复杂后端系统的工具暴露,为AI模型提供更加简洁和一致的接口。
例如,一个包含用户管理、订单处理和库存查询的复杂电子商务系统,可以通过外观模式提供一个统一的"MCP工具门面",隐藏内部实现的复杂性:
// 为复杂电商系统提供简化的MCP外观
@McpServerEndpoint(name = "ecommerce-facade")
public class EcommerceFacade {
private UserService userService;
private OrderService orderService;
private InventoryService inventoryService;
@ToolMapping(description = "处理用户订单")
public String processUserOrder(@Param String userId, @Param String productId) {
// 协调多个子系统的复杂逻辑
User user = userService.validateUser(userId);
Product product = inventoryService.checkAvailability(productId);
Order order = orderService.createOrder(user, product);
return "订单创建成功: " + order.getId();
}
}
4. 代理模式:MCP通信增强与控制
代理模式为其他对象提供一种代理以控制对这个对象的访问。在MCP应用中,代理模式可以用于实现访问控制、缓存、延迟加载和日志记录等横切关注点。
例如,可以为昂贵的MCP工具调用添加缓存代理,避免重复计算或重复外部API调用:
// 为天气查询工具添加缓存代理
public class CachedWeatherTool implements WeatherService {
private WeatherService realService;
private Cache cache;
public CachedWeatherTool(WeatherService realService) {
this.realService = realService;
this.cache = new Cache();
}
@Override
public WeatherData getWeather(String city) {
if (cache.contains(city)) {
return cache.get(city);
}
WeatherData data = realService.getWeather(city);
cache.put(city, data);
return data;
}
}
六、行为型模式在MCP交互流程中的应用
1. 模板方法模式:MCP操作标准化流程
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。在MCP应用中,模板方法模式可以标准化工具调用的处理流程,包括参数验证、权限检查、执行和结果处理等共同步骤。
// MCP工具执行的模板方法
public abstract class AbstractMcpTool {
// 模板方法定义了执行流程
public final McpResponse execute(McpRequest request) {
validateParameters(request);
checkPermissions(request);
Object result = doExecute(request);
return formatResponse(result);
}
protected abstract void validateParameters(McpRequest request);
protected abstract void checkPermissions(McpRequest request);
protected abstract Object doExecute(McpRequest request);
protected abstract McpResponse formatResponse(Object result);
}
2. 策略模式:多算法MCP工具实现
策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。在MCP应用中,策略模式允许动态选择工具的具体实现算法,使AI模型能够根据上下文选择最合适的处理策略。
例如,一个数据处理的MCP工具可以根据数据量大小选择不同的处理策略:
// 数据处理策略接口
public interface DataProcessingStrategy {
ProcessedResult process(LargeDataset dataset);
}
// 具体策略 - 内存处理(适用于小数据集)
public class InMemoryProcessingStrategy implements DataProcessingStrategy {
@Override
public ProcessedResult process(LargeDataset dataset) {
// 在内存中处理数据
return processedResult;
}
}
// 具体策略 - 分布式处理(适用于大数据集)
public class DistributedProcessingStrategy implements DataProcessingStrategy {
@Override
public ProcessedResult process(LargeDataset dataset) {
// 使用分布式计算框架处理
return processedResult;
}
}
// 使用策略的MCP工具
@McpServerEndpoint(name = "data-processor")
public class DataProcessingTool {
@ToolMapping(description = "处理数据集")
public String processData(@Param String datasetId) {
LargeDataset dataset = loadDataset(datasetId);
// 根据数据大小选择策略
DataProcessingStrategy strategy = dataset.size() > MAX_MEMORY_SIZE ?
new DistributedProcessingStrategy() :
new InMemoryProcessingStrategy();
return strategy.process(dataset);
}
}
3. 观察者模式:MCP状态变更通知
观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。在MCP应用中,观察者模式可以用于实现资源变更通知、工具可用性状态更新等场景。
例如,当MCP服务器的可用工具列表发生变化时,可以通过观察者模式通知所有连接的客户端:
// MCP工具注册表的观察者实现
public class ToolRegistry {
private List<ToolChangeListener> listeners = new ArrayList<>();
public void registerTool(McpTool tool) {
// 注册工具逻辑...
notifyToolAdded(tool);
}
public void addToolChangeListener(ToolChangeListener listener) {
listeners.add(listener);
}
private void notifyToolAdded(McpTool tool) {
for (ToolChangeListener listener : listeners) {
listener.onToolAdded(tool);
}
}
}
七、设计模式组合在复杂MCP场景中的应用
在实际的MCP应用开发中,设计模式很少单独使用,更多时候需要通过模式组合来解决复杂的设计问题。模式组合可能会导致所得结果不确定,严重影响软件产品的质量,因此需要谨慎的设计和形式化的组合方法。
1. 工厂+策略+组合模式:可扩展工具框架
这三种模式的组合可以构建一个高度可扩展的MCP工具框架。工厂负责创建工具实例,策略模式提供可互换的算法实现,组合模式则管理工具之间的层次关系。
例如,在一个文档处理MCP服务中,可以使用工厂方法创建不同的处理工具,策略模式为每个工具提供可配置的处理算法,组合模式则将这些工具组织成逻辑组,便于AI模型批量调用相关功能。
2. 外观+适配器+代理模式:系统集成最佳实践
这三种模式的组合为遗留系统集成提供了完整的解决方案。适配器模式将遗留系统接口转换为MCP兼容接口,外观模式提供简化的高层接口,代理模式则添加额外的控制和优化层。
这种组合在企业级MCP应用中特别有用,可以逐步将现有系统迁移到MCP架构,而不需要大规模重写现有代码。
八、Java MCP开发实战指南
1. 环境搭建与框架选择
Java MCP开发主要有两个框架选择:Spring AI MCP和Solon AI MCP。两者各有特点,选择取决于具体需求:
- Spring AI MCP:基于Spring生态,支持Java 17+,适合已经使用Spring堆栈的项目。
- Solon AI MCP:支持Java 8+,配置更简洁,支持多端点,适合轻量级部署和对老版本Java兼容性要求高的项目。
依赖配置示例(Solon-MCP):
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-ai-mcp</artifactId>
<version>3.2.0</version>
</dependency>
2. MCP服务器开发步骤
开发一个完整的MCP服务器包含以下关键步骤:
工具定义:使用注解声明MCP工具,提供清晰的描述和参数信息。
@McpServerEndpoint(name = "document-tools", sseEndpoint = "/mcp/sse")
public class DocumentProcessingTools {
@ToolMapping(description = "从PDF提取文本内容")
public String extractPdfText(@Param("filePath") String filePath) {
// 实现提取逻辑
return extractedText;
}
}
资源暴露:通过资源映射提供只读数据访问。
@ResourceMapping(uri = "documents://recent", description = "最近处理的文档列表")
public List<DocumentInfo> getRecentDocuments() {
return documentService.getRecentDocuments();
}
生命周期管理:正确处理服务器初始化和关闭逻辑,确保资源正确释放。
3. 企业级应用中的考量与最佳实践
在生产环境中部署MCP应用需要考虑以下方面:
- 安全设计:实现认证、授权和审计机制,确保只有授权的AI模型可以访问特定工具和资源。
- 性能优化:使用缓存、连接池和异步处理提高系统吞吐量。
- 可观测性:集成日志、监控和跟踪系统,便于问题诊断和性能分析。
九、综合实战案例:智能文档分析MCP服务
1. 案例背景与需求分析
随着企业数字化转型的深入,智能文档处理成为AIGC的重要应用场景。我们将开发一个智能文档分析MCP服务,支持多格式文档解析、内容分析和智能摘要生成。
核心需求包括:
- 支持PDF、Word、Markdown等常见格式解析
- 实现文档内容的结构化提取和关键信息识别
- 提供基于AIGC的文档摘要和内容分析能力
- 保证处理过程的高效性和可靠性
2. 系统架构设计
系统采用分层架构,明确各层职责和设计模式应用点:
- 传输层:使用SSE传输模式,通过适配器模式兼容不同客户端。
- 服务层:核心业务逻辑,运用工厂模式创建文档处理器,策略模式选择分析算法。
- 集成层:连接AI模型和外部存储,通过代理模式添加缓存和访问控制。
3. 核心代码实现
3.1 工具类设计与模式应用
文档处理器工厂(工厂模式)
// 文档处理器抽象接口
public interface DocumentProcessor {
Document process(String filePath) throws DocumentProcessingException;
boolean supports(String fileType);
}
// 工厂类负责创建合适的处理器
@Component
public class DocumentProcessorFactory {
private final Map<String, DocumentProcessor> processors = new HashMap<>();
@Autowired
public DocumentProcessorFactory(List<DocumentProcessor> processorList) {
for (DocumentProcessor processor : processorList) {
// 注册所有支持的文档类型
processors.put(getSupportedType(processor), processor);
}
}
public DocumentProcessor createProcessor(String filePath) {
String fileType = getFileExtension(filePath).toLowerCase();
DocumentProcessor processor = processors.get(fileType);
if (processor == null) {
throw new UnsupportedDocumentTypeException("不支持的文档类型: " + fileType);
}
return processor;
}
private String getFileExtension(String filePath) {
return filePath.substring(filePath.lastIndexOf(".") + 1);
}
}
// 具体处理器实现
@Component
public class PdfDocumentProcessor implements DocumentProcessor {
private static final Set<String> SUPPORTED_TYPES = Set.of("pdf");
@Override
public Document process(String filePath) {
try (PDDocument document = PDDocument.load(new File(filePath))) {
PDFTextStripper stripper = new PDFTextStripper();
String content = stripper.getText(document);
Document doc = new Document();
doc.setContent(content);
doc.setMetadata(extractMetadata(document));
doc.setWordCount(countWords(content));
return doc;
} catch (IOException e) {
throw new DocumentProcessingException("PDF处理失败", e);
}
}
@Override
public boolean supports(String fileType) {
return SUPPORTED_TYPES.contains(fileType.toLowerCase());
}
private Map<String, Object> extractMetadata(PDDocument document) {
Map<String, Object> metadata = new HashMap<>();
PDDocumentInformation info = document.getDocumentInformation();
metadata.put("title", info.getTitle());
metadata.put("author", info.getAuthor());
metadata.put("pageCount", document.getNumberOfPages());
metadata.put("creationDate", info.getCreationDate());
return metadata;
}
}
分析策略注册器(策略模式)
// 分析策略接口
public interface AnalysisStrategy {
AnalysisResult analyze(Document document);
String getStrategyName();
}
// 策略注册表
@Component
public class AnalysisStrategyRegistry {
private final Map<String, AnalysisStrategy> strategies = new ConcurrentHashMap<>();
@Autowired
public AnalysisStrategyRegistry(List<AnalysisStrategy> strategyList) {
for (AnalysisStrategy strategy : strategyList) {
strategies.put(strategy.getStrategyName(), strategy);
}
}
public AnalysisStrategy getStrategy(String strategyName) {
AnalysisStrategy strategy = strategies.get(strategyName);
if (strategy == null) {
throw new AnalysisStrategyNotFoundException("未知的分析策略: " + strategyName);
}
return strategy;
}
public Set<String> getAvailableStrategies() {
return strategies.keySet();
}
}
// 具体策略实现 - 摘要生成
@Component
public class SummaryAnalysisStrategy implements AnalysisStrategy {
private final AIService aiService;
@Override
public AnalysisResult analyze(Document document) {
String prompt = buildSummaryPrompt(document.getContent());
String summary = aiService.generateText(prompt);
AnalysisResult result = new AnalysisResult();
result.setSummary(summary);
result.setKeyPoints(extractKeyPoints(summary));
result.setAnalysisType("summary");
return result;
}
@Override
public String getStrategyName() {
return "summary";
}
private String buildSummaryPrompt(String content) {
return String.format("""
请为以下文档生成一个简洁的摘要,重点突出核心观点和关键信息:
%s
要求:
1. 摘要长度在200字以内
2. 包含主要论点和结论
3. 保持客观中立
""", content.substring(0, Math.min(content.length(), 4000)));
}
}
// 具体策略实现 - 关键信息提取
@Component
public class KeyInfoExtractionStrategy implements AnalysisStrategy {
@Override
public AnalysisResult analyze(Document document) {
String content = document.getContent();
AnalysisResult result = new AnalysisResult();
result.setKeyEntities(extractEntities(content));
result.setKeyPhrases(extractKeyPhrases(content));
result.setAnalysisType("keyinfo");
return result;
}
@Override
public String getStrategyName() {
return "keyinfo";
}
private List<String> extractEntities(String content) {
// 使用NLP库提取命名实体
return List.of("组织名", "人名", "地点"); // 简化实现
}
}
3.2 MCP工具端点实现
主工具类(组合模式 + 模板方法)
@McpServerEndpoint(name = "smart-document-analyzer", sseEndpoint = "/mcp/sse")
public class DocumentAnalysisTools {
private final DocumentProcessorFactory processorFactory;
private final AnalysisStrategyRegistry strategyRegistry;
private final DocumentResourceManager resourceManager;
private final AIService aiService;
// 工具执行模板
private <T> T executeWithTemplate(String operationName,
Supplier<T> operation,
Consumer<Exception> errorHandler) {
try {
log.info("开始执行文档分析操作: {}", operationName);
long startTime = System.currentTimeMillis();
T result = operation.get();
long duration = System.currentTimeMillis() - startTime;
log.info("文档分析操作完成: {}, 耗时: {}ms", operationName, duration);
return result;
} catch (Exception e) {
log.error("文档分析操作失败: {}", operationName, e);
errorHandler.accept(e);
throw e;
}
}
@ToolMapping(description = "分析文档并提取关键信息")
public AnalysisResult analyzeDocument(
@Param("filePath") String filePath,
@Param("analysisType") String analysisType) {
return executeWithTemplate("analyzeDocument", () -> {
// 使用工厂模式创建文档处理器
DocumentProcessor processor = processorFactory.createProcessor(filePath);
// 使用策略模式选择分析策略
AnalysisStrategy strategy = strategyRegistry.getStrategy(analysisType);
// 执行文档处理和分析
Document document = processor.process(filePath);
AnalysisResult result = strategy.analyze(document);
// 缓存处理结果
resourceManager.cacheDocument(document.getId(), document);
return result;
}, e -> {
throw new McpToolExecutionException("文档分析失败", e);
});
}
@ToolMapping(description = "批量处理文档文件夹")
public BatchProcessResult batchProcessDocuments(
@Param("folderPath") String folderPath,
@Param("analysisType") String analysisType) {
return executeWithTemplate("batchProcessDocuments", () -> {
File folder = new File(folderPath);
if (!folder.isDirectory()) {
throw new IllegalArgumentException("路径不是文件夹: " + folderPath);
}
List<AnalysisResult> results = new ArrayList<>();
List<String> errors = new ArrayList<>();
// 使用组合模式处理文件夹结构
processFolderRecursively(folder, analysisType, results, errors);
return new BatchProcessResult(results, errors);
}, e -> {
throw new McpToolExecutionException("批量处理失败", e);
});
}
@ToolMapping(description = "生成文档智能摘要")
public String generateDocumentSummary(@Param("filePath") String filePath) {
return executeWithTemplate("generateDocumentSummary", () -> {
DocumentProcessor processor = processorFactory.createProcessor(filePath);
Document document = processor.process(filePath);
// 使用AI服务生成智能摘要
String summaryPrompt = buildSummaryPrompt(document.getContent());
return aiService.generateText(summaryPrompt);
}, e -> {
throw new McpToolExecutionException("摘要生成失败", e);
});
}
private void processFolderRecursively(File folder, String analysisType,
List<AnalysisResult> results,
List<String> errors) {
File[] files = folder.listFiles();
if (files == null) return;
for (File file : files) {
if (file.isDirectory()) {
processFolderRecursively(file, analysisType, results, errors);
} else {
try {
AnalysisResult result = analyzeDocument(file.getAbsolutePath(), analysisType);
results.add(result);
} catch (Exception e) {
errors.add("处理文件失败: " + file.getName() + " - " + e.getMessage());
}
}
}
}
}
3.3 资源管理实现
资源管理器(单例模式 + 代理模式)
@Component
public class DocumentResourceManager {
private static volatile DocumentResourceManager instance;
private final Map<String, DocumentCache> cache;
private final ScheduledExecutorService cleanupExecutor;
@Autowired
private DocumentRepository documentRepository;
private DocumentResourceManager() {
this.cache = new ConcurrentHashMap<>();
this.cleanupExecutor = Executors.newScheduledThreadPool(1);
// 定期清理过期缓存
cleanupExecutor.scheduleAtFixedRate(this::cleanupExpiredCache, 1, 1, TimeUnit.HOURS);
}
public static DocumentResourceManager getInstance() {
if (instance == null) {
synchronized (DocumentResourceManager.class) {
if (instance == null) {
instance = new DocumentResourceManager();
}
}
}
return instance;
}
// 缓存代理实现
public void cacheDocument(String docId, Document document) {
DocumentCache cacheEntry = new DocumentCache(document, System.currentTimeMillis());
cache.put(docId, cacheEntry);
// 异步持久化到数据库
CompletableFuture.runAsync(() ->
documentRepository.save(DocumentEntity.fromDomain(document))
);
}
public Document getCachedDocument(String docId) {
DocumentCache cacheEntry = cache.get(docId);
if (cacheEntry != null && !cacheEntry.isExpired()) {
return cacheEntry.getDocument();
}
// 缓存未命中,从数据库加载
Optional<DocumentEntity> entity = documentRepository.findById(docId);
if (entity.isPresent()) {
Document document = entity.get().toDomain();
cacheDocument(docId, document); // 重新缓存
return document;
}
return null;
}
private void cleanupExpiredCache() {
long currentTime = System.currentTimeMillis();
cache.entrySet().removeIf(entry ->
entry.getValue().isExpired(currentTime)
);
}
// 缓存条目内部类
private static class DocumentCache {
private static final long CACHE_DURATION = 30 * 60 * 1000; // 30分钟
private final Document document;
private final long timestamp;
public DocumentCache(Document document, long timestamp) {
this.document = document;
this.timestamp = timestamp;
}
public boolean isExpired() {
return isExpired(System.currentTimeMillis());
}
public boolean isExpired(long currentTime) {
return currentTime - timestamp > CACHE_DURATION;
}
public Document getDocument() {
return document;
}
}
}
资源映射端点
@McpServerEndpoint(name = "document-resources")
public class DocumentResourceEndpoint {
@ResourceMapping(uri = "documents://recent", description = "最近处理的文档列表")
public List<DocumentInfo> getRecentDocuments() {
return DocumentResourceManager.getInstance()
.getRecentDocuments()
.stream()
.map(this::toDocumentInfo)
.collect(Collectors.toList());
}
@ResourceMapping(uri = "analysis://templates", description = "可用的分析模板")
public Map<String, Object> getAnalysisTemplates() {
Map<String, Object> templates = new HashMap<>();
templates.put("summary", createSummaryTemplate());
templates.put("keyinfo", createKeyInfoTemplate());
templates.put("sentiment", createSentimentTemplate());
return templates;
}
@ResourceMapping(uri = "system://metrics", description = "系统运行指标")
public SystemMetrics getSystemMetrics() {
SystemMetrics metrics = new SystemMetrics();
metrics.setActiveConnections(getActiveConnections());
metrics.setDocumentsProcessed(getDocumentsProcessedCount());
metrics.setAverageProcessingTime(getAverageProcessingTime());
metrics.setMemoryUsage(getMemoryUsage());
return metrics;
}
private DocumentInfo toDocumentInfo(Document document) {
DocumentInfo info = new DocumentInfo();
info.setId(document.getId());
info.setTitle(document.getMetadata().get("title").toString());
info.setProcessedAt(document.getProcessedAt());
info.setWordCount(document.getWordCount());
return info;
}
}
3.4 提示模板设计
提示模板管理器(模板方法模式)
@Component
public class PromptTemplateManager {
private final Map<String, PromptTemplate> templates = new HashMap<>();
@PostConstruct
public void initTemplates() {
// 注册预定义模板
registerTemplate("summary", new SummaryPromptTemplate());
registerTemplate("qa", new QAPromptTemplate());
registerTemplate("translation", new TranslationPromptTemplate());
}
public String renderTemplate(String templateName, Map<String, Object> context) {
PromptTemplate template = templates.get(templateName);
if (template == null) {
throw new TemplateNotFoundException("模板不存在: " + templateName);
}
return template.render(context);
}
private void registerTemplate(String name, PromptTemplate template) {
templates.put(name, template);
}
}
// 抽象模板基类
public abstract class PromptTemplate {
// 模板方法定义渲染流程
public final String render(Map<String, Object> context) {
validateContext(context);
String template = loadTemplate();
return processTemplate(template, context);
}
protected abstract void validateContext(Map<String, Object> context);
protected abstract String loadTemplate();
protected abstract String processTemplate(String template, Map<String, Object> context);
}
// 具体模板实现
public class SummaryPromptTemplate extends PromptTemplate {
@Override
protected void validateContext(Map<String, Object> context) {
if (!context.containsKey("content")) {
throw new IllegalArgumentException("摘要模板需要content参数");
}
}
@Override
protected String loadTemplate() {
return """
请为以下内容生成一个专业的技术摘要:
{content}
要求:
1. 提取核心技术点和创新点
2. 总结主要方法和实验结果
3. 指出可能的改进方向
4. 保持技术准确性
5. 字数控制在300-500字
请按照以下格式组织:
## 核心内容
{核心内容摘要}
## 技术方法
{技术方法总结}
## 主要发现
{主要发现总结}
""";
}
@Override
protected String processTemplate(String template, Map<String, Object> context) {
String content = (String) context.get("content");
// 对内容进行预处理,避免过长
String processedContent = content.length() > 8000 ?
content.substring(0, 8000) + "...[内容截断]" : content;
return template.replace("{content}", processedContent);
}
}
3.5 配置和启动类
MCP服务器配置
@Configuration
@EnableMcpServer
public class McpServerConfig {
@Bean
public McpServer mcpServer(List<McpServerEndpoint> endpoints) {
McpServerConfig config = McpServerConfig.builder()
.serverType("sse")
.sseEndpoint("/mcp/sse")
.build();
return new McpServer(config, endpoints);
}
@Bean
public AIService aiService() {
// 配置AI服务连接
return new OpenAIService(System.getenv("OPENAI_API_KEY"));
}
}
// 应用启动类
@SpringBootApplication
public class DocumentAnalysisApplication {
public static void main(String[] args) {
SpringApplication.run(DocumentAnalysisApplication.class, args);
}
@Bean
public CommandLineRunner initData(DocumentProcessorFactory factory,
AnalysisStrategyRegistry registry) {
return args -> {
log.info("文档分析MCP服务启动完成");
log.info("支持的文档类型: {}", factory.getSupportedTypes());
log.info("可用的分析策略: {}", registry.getAvailableStrategies());
};
}
}
更多推荐
所有评论(0)