利用javadoc注釋自動(dòng)生成Swagger注解
目的
可視化接口管理平臺(tái),通過(guò)接口管理平臺(tái)一目了然知道接口的作用,接口上面擴(kuò)展了哪些功能,從而做到對(duì)應(yīng)用程序代碼零侵入
現(xiàn)狀
由于現(xiàn)在controller方法上面沒(méi)有swagger注解,只能拿到接口url地址,無(wú)法獲得接口功能描述。
值得慶幸的是我們的代碼非常規(guī)范每個(gè)方法都有標(biāo)準(zhǔn)的javadoc注釋,于是就想到了獲取javadoc注釋的描述自動(dòng)生成swagger注解
思路
1,AOP切面,你會(huì)發(fā)現(xiàn)根本無(wú)法獲取javadoc,這個(gè)方案就直接pass掉了
2,讀文件流獲取文件內(nèi)容解析出想要的數(shù)據(jù),雖然可以但是費(fèi)時(shí)費(fèi)力,不是最優(yōu)的方案
3,使用開(kāi)源的javaparser難道不香嗎,非常香就它了
javaparser
<dependency> <groupId>com.github.javaparser</groupId> <artifactId>javaparser-core</artifactId> <version>3.25.4</version> </dependency>
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
import com.fuchuang.scm.common.util.ScmAssert;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
/**
* 通過(guò)javadoc注釋自動(dòng)生成Swagger注解
*
* @author xiongyan
* @date 2023/7/7
*/
public class Sw {
public static void main(String[] args) throws Exception {
String clientPath = "/Users/xiongyan/Documents/work/fc-cdc/fc-cdc-api/src/main/java/com/fc/cdc/controller";
String controllerPath = "/Users/xiongyan/Documents/work/fc-cdc/fc-cdc-api/src/main/java/com/fc/cdc/controller";
Map<String, ClassOrInterfaceDeclaration> clientMap = Sw.clientMap(clientPath);
File project = new File(controllerPath);
ScmAssert.isTrue(project.exists(), "項(xiàng)目路徑不存在");
for (File file : project.listFiles()) {
CompilationUnit cu = StaticJavaParser.parse(file);
ClassOrInterfaceDeclaration classDeclaration = cu.findFirst(ClassOrInterfaceDeclaration.class).orElse(null);
if (null == classDeclaration) {
continue;
}
String classComment = null;
Map<String, String> methodCommentMap = new HashMap<>();
if (classDeclaration.getImplementedTypes().size() > 0) {
String interfaceName = classDeclaration.getImplementedTypes(0).getNameAsString();
ClassOrInterfaceDeclaration interfaceDeclaration = clientMap.get(interfaceName);
if (null == interfaceDeclaration) {
continue;
}
if (interfaceDeclaration.getJavadoc().isPresent()) {
classComment = interfaceDeclaration.getJavadoc().get().getDescription().toText();
}
for (MethodDeclaration method : interfaceDeclaration.getMethods()) {
if (!method.getJavadoc().isPresent()) {
continue;
}
methodCommentMap.put(method.getNameAsString(), method.getJavadoc().get().getDescription().toText());
}
}
if (StrUtil.isEmpty(classComment)) {
if (classDeclaration.getJavadoc().isPresent()) {
classComment = classDeclaration.getJavadoc().get().getDescription().toText();
} else {
classComment = classDeclaration.getNameAsString();
}
}
if (!classDeclaration.getAnnotationByClass(Api.class).isPresent()) {
classDeclaration.addAndGetAnnotation(Api.class).addPair("value", "\"" + classComment + "\"");
}
for (MethodDeclaration method : classDeclaration.getMethods()) {
if (method.getAnnotationByClass(ApiOperation.class).isPresent()) {
continue;
}
String methodComment = methodCommentMap.get(method.getNameAsString());
if (StrUtil.isEmpty(methodComment)) {
if (method.getJavadoc().isPresent()) {
methodComment = method.getJavadoc().get().getDescription().toText();
} else {
methodComment = method.getNameAsString();
}
}
method.addAndGetAnnotation(ApiOperation.class).addPair("value", "\"" + classComment + "-" + methodComment + "\"");
}
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(cu.toString().getBytes());
}
}
private static Map<String, ClassOrInterfaceDeclaration> clientMap(String path) throws FileNotFoundException {
File project = new File(path);
ScmAssert.isTrue(project.exists(), "項(xiàng)目路徑不存在");
Map<String, ClassOrInterfaceDeclaration> clientMap = new HashMap<>();
for (File file : project.listFiles()) {
CompilationUnit cu = StaticJavaParser.parse(file);
if (null == cu) {
continue;
}
ClassOrInterfaceDeclaration classDeclaration = cu.findFirst(ClassOrInterfaceDeclaration.class).orElse(null);
if (null != classDeclaration && classDeclaration.isInterface()) {
clientMap.put(classDeclaration.getNameAsString(), classDeclaration);
}
}
return clientMap;
}
}效果
/**
* 數(shù)據(jù)庫(kù)管理
*
* @author xiongyan
* @date 2023/03/13
*/
@RestController
@RequestMapping("/web/database")
public class WebDatabaseController {
/**
* 分頁(yè)列表
*
* @param request
* @return
*/
@PostMapping("/page")
public ScmResponse<ScmPage<DatabaseDto>> page(@RequestBody DatabaseRequest request) {
return ScmResponseUtil.success();
}
}/**
* 數(shù)據(jù)庫(kù)管理
*
* @author xiongyan
* @date 2023/03/13
*/
@RestController
@RequestMapping("/web/database")
@Api(value = "數(shù)據(jù)庫(kù)管理")
public class WebDatabaseController {
/**
* 分頁(yè)列表
*
* @param request
* @return
*/
@PostMapping("/page")
@ApiOperation(value = "數(shù)據(jù)庫(kù)管理-分頁(yè)列表")
public ScmResponse<ScmPage<DatabaseDto>> page(@RequestBody DatabaseRequest request) {
return ScmResponseUtil.success();
}
}可以看到通過(guò)javadoc自動(dòng)生成了swagger注解,尤其是對(duì)于我們這樣微服務(wù)拆分很細(xì)的,應(yīng)用非常多,接口更是多的,通過(guò)這種方式效率最高的
接口文檔
服務(wù)器啟動(dòng)就可以把 接口應(yīng)用名稱,接口URL地址,接口請(qǐng)求方式,接口描述,接口請(qǐng)求結(jié)構(gòu),接口響應(yīng)結(jié)構(gòu) 數(shù)據(jù)收集進(jìn)行注冊(cè)自動(dòng)生成接口文檔
接口擴(kuò)展
1,數(shù)據(jù)脫敏
2,操作日志
3,防重復(fù)提交
4,接口冪等
5,接口限流
6,黑白名單
7,權(quán)限控制
8,。。。。。
到此這篇關(guān)于利用javadoc注釋自動(dòng)生成Swagger注解的文章就介紹到這了,更多相關(guān)javadoc生成Swagger注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java父線程(或是主線程)等待所有子線程退出的實(shí)例
下面小編就為大家分享一篇Java父線程(或是主線程)等待所有子線程退出的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11
使用通過(guò)ARP類似P2P終結(jié)者實(shí)現(xiàn)數(shù)據(jù)封包
目前網(wǎng)絡(luò)上類似P2P終結(jié)者這類軟件,主要都是基于ARP欺騙實(shí)現(xiàn)的,網(wǎng)絡(luò)上到處都有關(guān)于ARP的介紹,不過(guò)為了本文讀者不需要再去查找,我就在這里大概講解一下2012-12-12
Java字符串格式化,{}占位符根據(jù)名字替換實(shí)例
這篇文章主要介紹了Java字符串格式化,{}占位符根據(jù)名字替換實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
確保SpringBoot定時(shí)任務(wù)只執(zhí)行一次的常見(jiàn)方法小結(jié)
在Spring Boot項(xiàng)目中,確保定時(shí)任務(wù)只執(zhí)行一次是一個(gè)常見(jiàn)的需求,這種需求可以通過(guò)多種方式來(lái)實(shí)現(xiàn),以下是一些常見(jiàn)的方法,它們各具特點(diǎn),可以根據(jù)項(xiàng)目的實(shí)際需求來(lái)選擇最合適的方法,需要的朋友可以參考下2024-10-10
java返回的List進(jìn)行add操作報(bào)錯(cuò)
本文主要介紹了java返回的List進(jìn)行add操作報(bào)錯(cuò),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
SpringBoot和MyBatis環(huán)境下實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換過(guò)程
dynamic-datasource-spring-boot-starter?是一個(gè)用于在SpringBoot和MyBatis環(huán)境下實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的工具,它簡(jiǎn)化了配置和切換邏輯,通過(guò)引入依賴,配置數(shù)據(jù)源和使用@DS注解,可以輕松實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換2025-10-10

