如何让Eclipse LSP4e服务单实例化?避免新建Java文件重初始化
问题解决:Eclipse LSP4e语言服务重复初始化问题
核心问题分析
配置singleton="true"后仍出现重复初始化,根源在于:
- LSP4e框架通过反射实例化
CodecompProcessStreamConnectionProvider,不会调用你自定义的getInstance()方法,所以手写单例逻辑无效。 - 构造函数直接调用
init(),哪怕框架创建临时实例(singleton生效前的冗余操作)也会触发初始化。 ProcessStreamConnectionProvider的start()方法可能被多次调用,导致重复启动服务进程。
修复方案
1. 确认LSP4e的Singleton配置有效性
你已在plugin.xml中设置singleton="true",需额外检查:
- 服务器ID唯一,无重复定义的同ID服务
- contentTypeMapping仅关联到该服务ID,未映射其他服务
2. 重构连接提供者的初始化逻辑
修改CodecompProcessStreamConnectionProvider,让初始化逻辑全局仅执行一次,适配LSP4e的实例化机制:
public class CodecompProcessStreamConnectionProvider extends ProcessStreamConnectionProvider { // volatile保证多线程下初始化状态的可见性 private static volatile boolean globalInitialized = false; private static final Object initLock = new Object(); private static final long INIT_SERVER_TIMEOUT_SECONDS = 180; // 空构造函数,避免实例创建时触发重复初始化 public CodecompProcessStreamConnectionProvider() { } @Override public void start() throws IOException { // 双重检查锁,确保全局仅初始化一次 if (!globalInitialized) { synchronized (initLock) { if (!globalInitialized) { init(); globalInitialized = true; } } } // 检查命令是否已设置,避免重复调用super.start()启动多个进程 if (getCommands() == null || getCommands().isEmpty()) { super.start(); } } private void init() { Logger.debug("Initializing chat server asynchronously"); try { AsyncExecutor.executeWithTimeout(() -> { try { CodecompLocator locator = new CodecompLocator(); List<String> commands = new ArrayList<>(); String CodecompAgentFolderPath = locator.findAgent().toFile().getParent(); Logger.debug("Codecomp agent location: " + CodecompAgentFolderPath); File file = new File(CodecompAgentFolderPath + "/node"); file.setReadable(true); file.setExecutable(true); commands.add(CodecompAgentFolderPath + "/node"); commands.add("language-server.js"); commands.add("--stdio"); this.setCommands(commands); this.setWorkingDirectory(CodecompAgentFolderPath); Logger.debug("Chat server initialized successfully"); } catch (Exception e) { Logger.error(e, Messages.CodecompProcessStreamConnectionProvider_ErrorStartingChatLanguageServer); throw new RuntimeException("Error initializing chat server", e); } }, INIT_SERVER_TIMEOUT_SECONDS, TimeUnit.SECONDS); } catch (TimeoutException | ExecutionException | InterruptedException e) { Logger.error(e, Messages.CodecompProcessStreamConnectionProvider_ErrorStartingChatLanguageServer); } } // 保留原有方法实现 protected ProcessBuilder createProcessBuilder() { ProcessBuilder builder = super.createProcessBuilder(); builder.environment().put("Codecomp_AGENT_LOGMESSAGE", "true"); Map<String, String> proxySettings = CodecompProxyUtil.getProxyVars(); proxySettings.forEach(builder.environment()::put); return builder; } public Object getInitializationOptions(URI rootUri) { CodecompInitializationOptions options = CodecompManager.getInstance().createInitializationOptions(); return options; } public void handleMessage(Message message, LanguageServer languageServer, URI rootURI) { super.handleMessage(message, languageServer, rootURI); } }
3. 排查服务意外关闭的触发点
检查是否存在以下导致服务shutdown的逻辑:
- 是否监听文件关闭事件时主动调用服务shutdown方法
- 客户端实现
codecompChatLanguageClientImpl中是否有异常触发服务关闭的逻辑
关键调整说明
- 移除自定义单例实例,改用全局初始化标记+双重检查锁,适配LSP4e的反射实例化机制
- 将初始化逻辑从构造函数移到
start()方法,确保服务真正启动时才执行初始化 - 增加命令存在性检查,避免多次调用
super.start()启动多个进程 - 保留LSP4e原生
singleton="true"配置,让框架负责管理服务实例的全局唯一性
内容的提问来源于stack exchange,提问作者jasvik
相关产品推荐
相关产品推荐

