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 MCPSolon 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());
        };
    }
}
Logo

更多推荐