如何独立启动VSCode Java Language Server提取不可编译Java项目调用图
独立启动VS Code Java Language Server并提取调用图指南
一、获取Java Language Server(JLS)程序
VS Code的Red Hat官方Java扩展内置了独立的JLS,可直接从已安装的扩展目录提取:
- 找到扩展安装路径:Windows为
%USERPROFILE%\.vscode\extensions\redhat.java-<版本号>,Linux/macOS为~/.vscode/extensions/redhat.java-<版本号> - 进入
server目录,提取jdt-language-server-<版本号>.jar,这是JLS核心运行程序 - 同时复制对应系统的
config_<系统>目录(如Linux对应config_linux),用于JLS的配置加载
二、启动Java Language Server
使用Java命令直接启动JLS,默认通过标准输入输出(stdio)与客户端通信:
# 替换为你的实际路径 java -jar /path/to/jdt-language-server-<版本号>.jar \ -configuration /path/to/config_<系统> \ -data /path/to/temp-workspace-dir
-data指定的临时工作目录用于存储JLS的索引缓存,建议每次运行新建空目录,避免遗留数据干扰
三、编写LSP客户端与JLS交互
LSP基于JSON-RPC协议,推荐使用eclipse-lsp4j库编写Java客户端,核心步骤与示例如下:
1. 引入依赖(Maven)
<dependencies> <dependency> <groupId>org.eclipse.lsp4j</groupId> <artifactId>org.eclipse.lsp4j</artifactId> <version>0.21.1</version> </dependency> <dependency> <groupId>org.eclipse.lsp4j</groupId> <artifactId>org.eclipse.lsp4j.jsonrpc</artifactId> <version>0.21.1</version> </dependency> </dependencies>
2. 客户端核心代码示例
import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.Launcher; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Path; import java.util.Collections; import java.util.concurrent.CompletableFuture; public class JLSCallGraphClient { public static void main(String[] args) throws IOException { // 1. 启动JLS进程 Process jlsProcess = new ProcessBuilder( "java", "-jar", "/path/to/jdt-language-server.jar", "-configuration", "/path/to/config_linux", "-data", "/path/to/temp-workspace" ).start(); InputStream jlsIn = jlsProcess.getInputStream(); OutputStream jlsOut = jlsProcess.getOutputStream(); // 2. 创建LSP客户端与服务端连接 Launcher<LanguageServer> launcher = Launcher.createLauncher( new LanguageClient() { // 监听JLS日志,用于判断索引进度 @Override public void logMessage(LogMessageParams params) { System.out.println("[JLS Log] " + params.getMessage()); } @Override public void showMessage(ShowMessageParams params) { System.out.println("[JLS Message] " + params.getMessage()); } // 其他默认方法留空即可 }, LanguageServer.class, jlsIn, jlsOut ); LanguageServer languageServer = launcher.getRemoteProxy(); launcher.startListening(); // 3. 发送初始化请求,指定目标项目路径 InitializeParams initParams = new InitializeParams(); String projectUri = Path.of("/path/to/your-java-project").toUri().toString(); initParams.setRootUri(projectUri); initParams.setWorkspaceFolders(Collections.singletonList( new WorkspaceFolder(projectUri, "Target Project") )); CompletableFuture<InitializeResult> initResult = languageServer.initialize(initParams); initResult.join(); // 4. 发送初始化完成通知 languageServer.initialized(new InitializedParams()); // 5. 等待JLS完成项目索引(可根据日志判断,比如出现"Finished indexing workspace") try { Thread.sleep(10000); // 按需调整等待时间,或监听进度事件替代固定休眠 } catch (InterruptedException e) { e.printStackTrace(); } // 6. 提取调用图:以目标方法为例,获取其向外调用关系 // 示例:目标方法在Test.java的第10行第5列(LSP位置从0开始计数,所以填9,4) TextDocumentPositionParams positionParams = new TextDocumentPositionParams(); positionParams.setTextDocument(new TextDocumentIdentifier( Path.of("/path/to/your-java-project/src/main/java/com/example/Test.java").toUri().toString() )); positionParams.setPosition(new Position(9, 4)); // 调用JLS扩展方法获取向外调用 CompletableFuture<CallHierarchyOutgoingCall[]> outgoingCalls = languageServer .sendRequest("java/callHierarchy/outgoingCalls", positionParams) .thenApply(response -> (CallHierarchyOutgoingCall[]) response); // 处理返回结果,构建调用图 outgoingCalls.thenAccept(calls -> { for (CallHierarchyOutgoingCall call : calls) { System.out.println("被调用方法: " + call.getTo().getName()); for (Range range : call.getFromRanges()) { System.out.println("调用位置: 行" + (range.getStart().getLine() + 1) + ", 列" + (range.getStart().getCharacter() + 1)); } } }).join(); // 7. 关闭连接与进程 languageServer.shutdown().join(); languageServer.exit(); jlsProcess.destroy(); } }
四、关键注意事项
- 不可编译项目适配:JLS支持增量解析存在编译错误的代码,只要语法结构基本完整,就能提取大部分调用关系,无需项目完全可编译
- 扩展方法说明:JLS提供了Java特有的LSP扩展方法,除
java/callHierarchy/outgoingCalls外,java/callHierarchy/incomingCalls可获取方法被调用关系,java/project/getTypes可遍历项目类结构 - 索引等待优化:建议通过监听
window/logMessage事件中的索引完成日志,替代固定休眠时间,确保数据准确性 - 版本兼容性:确保
lsp4j版本与JLS版本匹配,避免协议不兼容问题
内容的提问来源于stack exchange,提问作者tlx0001
相关产品推荐
相关产品推荐

