JavaParser基于注释生成接口文档

前言

swagger接口文档侵入性比较强, 本来计划基于java注释做一个开源组件。经过调研选择了 JavaParser, 奈何解决不了jar包的注释问题, 所以最终放弃了, 仅记录下调研过程中的单元测试~

基于javaparser-core实现

maven依赖

1
2
3
4
5
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.22.1</version>
</dependency>

提取注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package cn.idea360.docs;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.comments.Comment;
import com.github.javaparser.javadoc.Javadoc;
import com.github.javaparser.javadoc.JavadocBlockTag;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.util.List;
import java.util.Optional;

/**
* @author cuishiying
* @date 2021-01-22
*/
public class JavaParserTest {

CompilationUnit cu;
@BeforeEach
public void runBeforeTestMethod() throws Exception{
File javaFile = new File("/Users/cuishiying/work/rest-docs/src/main/java/cn/idea360/docs/controller/UserController.java");
JavaParser javaParser = new JavaParser();
ParseResult<CompilationUnit> parseResult = javaParser.parse(javaFile);
parseResult.ifSuccessful(compilationUnit -> {
cu = compilationUnit;
});

}

@Test
void t1() {
// 获取所有注释信息
List<Comment> comments = cu.getComments();
System.out.println(comments);
}

@Test
void t2() {
for (Comment comment : cu.getAllContainedComments()) {
// 获取注释内容
String content = comment.getContent();
System.out.println(content);
}
}

@Test
void t3() {
for (Comment comment : cu.getAllContainedComments()) {
// 解析注释
Javadoc parse = comment.asJavadocComment().parse();
String methodDesc = parse.getDescription().toText();
System.out.println("方法注释:" + methodDesc);
List<JavadocBlockTag> blockTags = parse.getBlockTags();
for (JavadocBlockTag javadocBlockTag: blockTags) {
Optional<String> name = javadocBlockTag.getName();
name.ifPresent(k -> {
String text = javadocBlockTag.getContent().getElements().get(0).toText();
System.out.println("参数注释: " + k + ":" + text);
});
}
}
}

@Test
void t4() {
Optional<ClassOrInterfaceDeclaration> userController = cu.getClassByName("UserController");
userController.ifPresent(c -> {
c.getJavadoc().ifPresent(javadoc -> {
// 解析注释
String methodDesc = javadoc.getDescription().toText();
System.out.println("方法注释:" + methodDesc);
List<JavadocBlockTag> blockTags = javadoc.getBlockTags();
for (JavadocBlockTag javadocBlockTag: blockTags) {
String name = javadocBlockTag.getTagName();
String content = javadocBlockTag.getContent().toText();
System.out.println("参数注释: " + name + ":" + content);
}
});
});
}

}

基于therapi-runtime-javadoc实现

maven依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc</artifactId>
<version>0.12.0</version>
</dependency>
<dependency>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.12.0</version>
</dependency>

示例controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package cn.idea360.docs.controller;

import cn.idea360.docs.model.User;
import org.springframework.web.bind.annotation.*;

/**
* 接口注释示例
*/
@RestController
public class ExampleController {

/**
* 示例方法,用于展示API文档生成
*
* @param id 主键
* @param name 用户名
* @param age 用户年龄
*/
@GetMapping("/example/{id}")
public String exampleMethod(@PathVariable Long id, @RequestParam String name, @RequestParam int age, @RequestBody User user) {
return "Hello " + name + ", age " + age;
}
}

注释提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package cn.idea360.docs;

import cn.idea360.docs.controller.ExampleController;
import com.github.therapi.runtimejavadoc.ClassJavadoc;
import com.github.therapi.runtimejavadoc.MethodJavadoc;
import com.github.therapi.runtimejavadoc.ParamJavadoc;
import com.github.therapi.runtimejavadoc.RuntimeJavadoc;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.web.bind.annotation.GetMapping;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ApiDocGenerator {

public static void main(String[] args) {
try {
// 获取Controller类的实例
Class<?> clazz = ExampleController.class;
ClassJavadoc classJavadoc = RuntimeJavadoc.getJavadoc(clazz);
System.out.println("Class: " + classJavadoc.getComment());
System.out.println();

// 遍历类中所有的方法
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(GetMapping.class)) {
// 获取方法的Javadoc注释
MethodJavadoc methodJavadoc = RuntimeJavadoc.getJavadoc(method);

// 打印方法名称
System.out.println("Method: " + method.getName());

// 打印方法的Javadoc注释
System.out.println("Description: " + methodJavadoc.getComment());
System.out.println();

// 使用Spring的ParameterNameDiscoverer获取参数名
ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer();
String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);

// 获取方法的参数
Parameter[] parameters = method.getParameters();

// 打印参数名和参数对象
if (parameterNames != null && parameters.length == parameterNames.length) {
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameterNames[i];
System.out.println("Parameter name: " + parameterName);
System.out.println("Parameter type: " + parameter.getType().getName());
// 打印参数的Javadoc注释
Map<String, ParamJavadoc> paramJavadocMap = methodJavadoc.getParams().stream().collect(Collectors.toMap(ParamJavadoc::getName, Function.identity()));
System.out.println("Parameter description: " + (Objects.isNull(paramJavadocMap.get(parameterName)) ? "No description" : paramJavadocMap.get(parameterName).getComment()));
System.out.println();
}
} else {
System.out.println("No parameter names found or parameters length mismatch.");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

最后

本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】支持一下。