373 lines
8.2 KiB
Markdown
373 lines
8.2 KiB
Markdown
# jdtls (Java Language Server) 使用指南
|
||
|
||
> 通过 Serena MCP 工具调用 jdtls 进行 Java 代码分析
|
||
|
||
## 前置条件
|
||
|
||
### 1. 确保 jdtls 已安装
|
||
|
||
jdtls 通常通过 VS Code 的 Java 扩展自动安装,或者手动安装:
|
||
|
||
```bash
|
||
# 检查 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
|
||
|
||
### 激活项目
|
||
|
||
```yaml
|
||
# Step 1: 激活 Java 项目
|
||
tool: mcp__serena__activate_project
|
||
params:
|
||
project: "/path/to/java/project"
|
||
|
||
# 返回: 项目已激活,jdtls 已初始化
|
||
```
|
||
|
||
### 验证配置
|
||
|
||
```yaml
|
||
# 检查当前配置
|
||
tool: mcp__serena__get_current_config
|
||
|
||
# 确认输出包含:
|
||
# - active_project: /path/to/java/project
|
||
# - language_server: jdtls
|
||
```
|
||
|
||
---
|
||
|
||
## 符号提取操作
|
||
|
||
### 获取文件符号概览
|
||
|
||
```yaml
|
||
# 获取单个文件的符号列表(不含代码体)
|
||
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]
|
||
```
|
||
|
||
### 查找符号
|
||
|
||
```yaml
|
||
# 按名称模式查找符号
|
||
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]" -> 匹配重载方法的第一个
|
||
```
|
||
|
||
### 获取符号详情(含代码体)
|
||
|
||
```yaml
|
||
# 获取完整的符号定义(含代码)
|
||
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 注释
|
||
# - 完整方法体
|
||
# - 行号范围
|
||
```
|
||
|
||
---
|
||
|
||
## 引用分析操作
|
||
|
||
### 查找符号引用
|
||
|
||
```yaml
|
||
# 查找某个符号被哪些地方引用
|
||
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)
|
||
|
||
```yaml
|
||
# 当 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)
|
||
|
||
```python
|
||
# 伪代码: 递归构建调用链
|
||
|
||
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
|
||
}
|
||
```
|
||
|
||
### 实际操作步骤
|
||
|
||
```yaml
|
||
# 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 特有处理
|
||
|
||
### 注解识别
|
||
|
||
```yaml
|
||
# 常见注解及其语义含义
|
||
|
||
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 处理
|
||
|
||
```yaml
|
||
# 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 映射
|
||
|
||
```yaml
|
||
# 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` 补充
|