Files

8.2 KiB
Raw Permalink Blame History

jdtls (Java Language Server) 使用指南

通过 Serena MCP 工具调用 jdtls 进行 Java 代码分析

前置条件

1. 确保 jdtls 已安装

jdtls 通常通过 VS Code 的 Java 扩展自动安装,或者手动安装:

# 检查 jdtls 路径
which jdtls
# 或
ls ~/.vscode/extensions/redhat.java-*/server/

2. 项目结构要求

jdtls 需要以下文件来识别 Java 项目:

  • Maven: pom.xml
  • Gradle: build.gradle 或 build.gradle.kts
  • Eclipse: .project 和 .classpath

通过 Serena 使用 jdtls

激活项目

# Step 1: 激活 Java 项目
tool: mcp__serena__activate_project
params:
  project: "/path/to/java/project"

# 返回: 项目已激活,jdtls 已初始化

验证配置

# 检查当前配置
tool: mcp__serena__get_current_config

# 确认输出包含:
# - active_project: /path/to/java/project
# - language_server: jdtls

符号提取操作

获取文件符号概览

# 获取单个文件的符号列表(不含代码体)
tool: mcp__serena__get_symbols_overview
params:
  relative_path: "src/main/java/com/example/OrderController.java"
  depth: 1      # 0=仅顶层, 1=含直接成员, 2=含嵌套成员

# 返回示例:
# Classes:
#   - OrderController (class) [59-1241]
# Methods:
#   - getOrderList (method) [120-145]
#   - createOrder (method) [150-180]
#   - updateOrder (method) [185-210]

查找符号

# 按名称模式查找符号
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "OrderController"    # 可以是部分名称
  relative_path: "src/main/java/"         # 限定搜索范围
  include_body: false                     # 是否包含代码体
  include_info: true                      # 是否包含 hover 信息
  depth: 1                                # 包含成员的深度

# 名称模式规则:
# - "OrderController"           -> 匹配任何包含此名称的符号
# - "controller/OrderController" -> 匹配此路径后缀
# - "/com.example.OrderController" -> 精确匹配完整路径
# - "OrderController/getOrderList" -> 匹配类中的方法
# - "OrderController[0]"        -> 匹配重载方法的第一个

获取符号详情(含代码体)

# 获取完整的符号定义(含代码)
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "OrderService/saveOrder"
  relative_path: "src/main/java/com/example/service/OrderService.java"
  include_body: true
  depth: 0

# 返回包含:
# - 方法签名
# - Javadoc 注释
# - 完整方法体
# - 行号范围

引用分析操作

查找符号引用

# 查找某个符号被哪些地方引用
tool: mcp__serena__find_referencing_symbols
params:
  name_path: "OrderService/saveOrder"
  relative_path: "src/main/java/com/example/service/OrderService.java"
  include_info: true

# 返回示例:
# References (5 found):
#   - OrderController.createOrder [180:12-180:35]
#     snippet: "orderService.saveOrder(dto)"
#   - OrderController.updateOrder [210:12-210:35]
#     snippet: "orderService.saveOrder(updated)"
#   - OrderServiceTest.testSave [45:8-45:30]
#     snippet: "service.saveOrder(testDto)"

模式搜索(补充 LSP)

# 当 LSP 无法找到时,使用模式搜索
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "orderService\\.save"
  paths_include_glob: "**/*.java"
  restrict_search_to_code_files: true
  context_lines_before: 2
  context_lines_after: 2

# 适用场景:
# - 动态调用 (反射)
# - 字符串拼接的方法名
# - 注解中的引用

调用链构建

构建向下调用链 (callee)

# 伪代码: 递归构建调用链

def build_callee_chain(symbol_path, file_path, depth, max_depth):
    if depth > max_depth:
        return None

    # Step 1: 获取方法体
    symbol = find_symbol(
        name_path_pattern=symbol_path,
        relative_path=file_path,
        include_body=True
    )

    # Step 2: 在方法体中查找调用
    # 使用 LSP 或正则匹配
    calls = extract_method_calls(symbol.body)

    # Step 3: 对每个调用递归
    callees = []
    for call in calls:
        # 尝试定位被调用方法
        target = find_symbol(
            name_path_pattern=call.method_name,
            relative_path=call.potential_file
        )
        if target:
            callees.append({
                'symbol': target,
                'evidence': call.location,
                'children': build_callee_chain(
                    target.path, target.file, depth + 1, max_depth
                )
            })

    return {
        'symbol': symbol,
        'callees': callees
    }

实际操作步骤

# 1. 获取入口方法
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "OrderController/createOrder"
  relative_path: "src/main/java/com/example/controller/"
  include_body: true
  depth: 0

# 2. 分析方法体中的调用
# 从返回的 body 中提取: orderService.saveOrder(dto)

# 3. 定位被调用方法
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "OrderService/saveOrder"
  relative_path: "src/main/java/"
  include_body: true

# 4. 查找该方法的引用(验证调用关系)
tool: mcp__serena__find_referencing_symbols
params:
  name_path: "OrderService/saveOrder"
  relative_path: "src/main/java/com/example/service/OrderService.java"

# 5. 递归处理下一层

Java 特有处理

注解识别

# 常见注解及其语义含义

API 入口点注解:
  - "@RestController": REST API 控制器
  - "@Controller": MVC 控制器
  - "@RequestMapping": 请求映射
  - "@GetMapping": GET 请求
  - "@PostMapping": POST 请求
  - "@PutMapping": PUT 请求
  - "@DeleteMapping": DELETE 请求

服务层注解:
  - "@Service": 业务服务
  - "@Component": 通用组件
  - "@Transactional": 事务方法

数据层注解:
  - "@Repository": 数据访问
  - "@Mapper": MyBatis Mapper
  - "@Table": JPA 表映射
  - "@Entity": JPA 实体

数据对象注解:
  - "@Data": Lombok 数据类
  - "@Getter/@Setter": Lombok 访问器
  - "@Builder": Lombok 构建器

MyBatis Mapper 处理

# MyBatis Mapper 接口无法通过 LSP 追踪到 SQL
# 需要关联 XML 文件

# 1. 找到 Mapper 接口
tool: mcp__serena__find_symbol
params:
  name_path_pattern: "OrderMapper"
  relative_path: "src/main/java/"

# 2. 查找对应的 XML
tool: mcp__serena__search_for_pattern
params:
  substring_pattern: "OrderMapper"
  paths_include_glob: "**/*.xml"

# 3. 读取 XML 获取 SQL 定义
tool: mcp__serena__list_dir
params:
  relative_path: "src/main/resources/mapper/"

LSP SymbolKind 映射

# jdtls 返回的 SymbolKind 数值对照

1: File
2: Module
3: Namespace
4: Package
5: Class           # 类
6: Method          # 方法
7: Property
8: Field           # 字段
9: Constructor     # 构造函数
10: Enum           # 枚举
11: Interface      # 接口
12: Function       # 函数
13: Variable       # 变量
14: Constant       # 常量
15: String
16: Number
17: Boolean
18: Array
19: Object
20: Key
21: Null
22: EnumMember     # 枚举值
23: Struct
24: Event
25: Operator
26: TypeParameter  # 泛型参数

常见问题

Q: jdtls 启动慢

A: 首次打开项目时,jdtls 需要构建索引。
   - Maven 项目: 会下载依赖
   - 大项目: 索引可能需要几分钟
   建议: 等待 Serena 报告项目就绪

Q: 找不到符号

A: 可能原因:
   1. 项目未正确识别 (缺少 pom.xml 或 build.gradle)
   2. 编译错误导致索引不完整
   3. 符号在排除目录中

解决:
   1. 确保项目根目录正确
   2. 先执行 mvn compile 或 gradle build
   3. 使用 search_for_pattern 作为备选

Q: 引用结果不完整

A: jdtls 的 references 可能遗漏:
   1. 反射调用
   2. 字符串拼接的方法名
   3. 动态代理

解决:
   使用 search_for_pattern 补充搜索

最佳实践

  1. 先概览后详细 - 用 get_symbols_overview 了解文件结构,再用 find_symbol 获取详情
  2. 限定搜索范围 - 总是传入 relative_path 以提高效率
  3. 分批处理 - 大项目分模块处理,避免一次性加载全部
  4. 缓存结果 - 符号信息变化不频繁,可以缓存复用
  5. 结合搜索 - LSP 不足时用 search_for_pattern 补充