Skip to content

上文我们看到可以通过Swagger系列可以快速生成API文档, 但是这种API文档生成是需要在接口上添加注解等,这表明这是一种侵入式方式; 那么有没有非侵入式方式呢, 比如通过注释生成文档? 本文主要介绍非侵入式的方式及集成Smart-doc案例。我们构建知识体系时使用Smart-doc这类工具并不是目标,而是要了解非侵入方式能做到什么程度和技术思路 , 最后平衡 下来多数情况下多数人还是会选择Swagger+openapi技术栈的。 @anarkh

  • SpringBoot接口 - 如何生成接口文档之集成Smart-Doc
  • 准备知识点
  • 为什么会产生Smart-Doc这类工具?
  • 什么是Smart-Doc?有哪些特性?
  • 实现案例
  • 配置
  • 运行测试
  • 生成更多类型的文档
  • 进一步理解
  • 注释信息是有限的,smart-doc如何从注释拓展文档内容呢?
  • Maven多模块中使用插件有没有比较好的实践?
  • 如果生成文档时遇到问题,该如何调试?
  • 示例源码

准备知识点

需要了解Swagger侵入性和依赖性, 以及Smart-Doc这类工具如何解决这些问题, 部分内容来自官方网站在新窗口打开。@anarkh

为什么会产生Smart-Doc这类工具?

既然有了Swagger, 为何还会产生Smart-Doc这类工具呢? 本质上是Swagger侵入性和依赖性。

我们来看下目前主流的技术文档工具存在什么问题:

  1. 侵入性强 ,需要编写大量注解,代表工具如:swagger,还有一些公司自研的文档工具
  2. 强依赖性 ,如果项目不想使用该工具,业务代码无法编译通过。
  3. 代码解析能力弱,使用文档不齐全,主要代表为国内众多开源的相关工具。
  4. 众多基于注释分析的工具无法解析jar包里面的注释(sources jar包),需要人工配置源码路径,无法满足DevOps构建场景。
  5. 部分工具无法支持多模块复杂项目代码分析。

什么是Smart-Doc?有哪些特性?

smart-doc是一款同时支持JAVA REST API和Apache Dubbo RPC接口文档生成的工具,smart-doc在业内率先提出基于JAVA泛型定义推导 的理念, 完全基于接口源码来分析生成接口文档,不采用任何注解侵入到业务代码中。你只需要按照java-doc标准 编写注释, smart-doc就能帮你生成一个简易明了的Markdown、HTML5、Postman Collection2.0+、OpenAPI 3.0+的文档。

  • 零注解、零学习成本、只需要写标准JAVA注释。
  • 基于源代码接口定义自动推导,强大的返回结构推导。
  • 支持Spring MVC、Spring Boot、Spring Boot Web Flux(controller书写方式)、Feign。
  • 支持Callable、Future、CompletableFuture等异步接口返回的推导。
  • 支持JavaBean上的JSR303参数校验规范,包括分组验证。
  • 对JSON请求参数的接口能够自动生成模拟JSON参数。
  • 对一些常用字段定义能够生成有效的模拟值。
  • 支持生成JSON返回值示例。
  • 支持从项目外部加载源代码来生成字段注释(包括标准规范发布的jar包)。
  • 支持生成多种格式文档:Markdown、HTML5、Asciidoctor、Postman Collection、OpenAPI 3.0。 Up- 开放文档数据,可自由实现接入文档管理系统。
  • 支持导出错误码和定义在代码中的各种字典码到接口文档。
  • 支持Maven、Gradle插件式轻松集成。
  • 支持Apache Dubbo RPC接口文档生成。
  • debug接口调试html5页面完全支持文件上传,下载(@download tag标记下载方法)测试。

实现案例

从smart-doc 1.7.9开始官方提供了Maven插件,可以通过在项目中集成smart-doc的Maven插件,然后运行插件直接生成文档。 我们的案例基于smart-doc-maven-plugin,生成文档。示例参考官方配置文档在新窗口打开而写。

配置

添加maven的插件

xml
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>com.github.shalousun</groupId>
            <artifactId>smart-doc-maven-plugin</artifactId>
            <version>2.4.8</version>
            <configuration>
                
                <configFile>./src/main/resources/smart-doc.json</configFile>
                
                
                
                
                <excludes>
                    
                    
                    <exclude>com.alibaba:fastjson</exclude>
                </excludes>
                
                
                <includes>
                    
                    
                    <include>com.alibaba:fastjson</include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    
                    <phase>compile</phase>
                    <goals>
                        
                        <goal>html</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

其中./src/main/resources/smart-doc.json是配置文件。

json
{
  "serverUrl": "http://127.0.0.1", 
  "pathPrefix": "", 
  "isStrict": false, 
  "allInOne": true,  
  "outPath": "D://md2", 
  "coverOld": true,  
  "createDebugPage": true,
  "packageFilters": "",
  "md5EncryptedHtmlName": false,
  "style":"xt256", 
  "projectName": "anarkh-springboot-demo-smart-doc",
  "skipTransientField": true,
  "sortByTitle":false,
  "showAuthor":true,
  "requestFieldToUnderline":true,
  "responseFieldToUnderline":true,
  "inlineEnum":true,
  "recursionLimit":7,
  "allInOneDocFileName":"index.html",
  "requestExample":"true",
  "responseExample":"true",

  "ignoreRequestParams":[ 
    "org.springframework.ui.ModelMap"
  ],
  "dataDictionaries": [{ 
    "title": "http状态码字典", 
    "enumClassName": "tech.anarkh.springboot.smartdoc.constant.ResponseStatus", 
    "codeField": "responseCode",
    "descField": "description"
  }],
  "errorCodeDictionaries": [{ 
    "title": "title",
    "enumClassName": "tech.anarkh.springboot.smartdoc.constant.ResponseStatus", 
    "codeField": "responseCode",
    "descField": "description"
  }],
  "revisionLogs": [{ 
    "version": "1.1", 
    "revisionTime": "2022-07-01 22:12:01", 
    "status": "update", 
    "author": "anarkh", 
    "remarks": "init user api" 
  },{ 
    "version": "1.2", 
    "revisionTime": "2022-07-01 22:12:02", 
    "status": "update", 
    "author": "anarkh", 
    "remarks": "add address api" 
  }
  ],
  "customResponseFields": [{ 
    "name": "code",
    "desc": "响应代码",
    "ownerClassName": "org.springframework.data.domain.Pageable", 
    "ignore":true, 
    "value": "00000"
  }],
  "requestHeaders": [{ 
    "name": "token",
    "type": "string",
    "desc": "desc",
    "value":"token请求头的值",
    "required": false,
    "since": "-",
    "pathPatterns": "/app/test/**",
    "excludePathPatterns":"/app/page/**"
  },{
    "name": "appkey",
    "type": "string",
    "desc": "desc",
    "value":"appkey请求头的值",
    "required": false,
    "pathPatterns": "/test/add,/testConstants/1.0",
    "since": "-"
  }],
  "requestParams": [ 
    {
      "name": "configPathParam",
      "type": "string",
      "desc": "desc",
      "paramIn": "path", 
      "value":"testPath",
      "required": false,
      "since": "2.2.3",
      "pathPatterns": "/app/test/**",
      "excludePathPatterns":"/app/page/**"
    }],
  "responseBodyAdvice":{ 
    "className":"tech.anarkh.springboot.smartdoc.entity.ResponseResult" 
  }
}

运行测试

可以通过Maven命令生成文档

bash
//生成html
mvn -Dfile.encoding=UTF-8 smart-doc:html

在IDEA中,也可以通过maven插件构建

maven构建日志如下

text
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------< tech.anarkh:115-springboot-demo-smart-doc >---------------
[INFO] Building 115-springboot-demo-smart-doc 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] >>> smart-doc-maven-plugin:2.4.8:html (default-cli) > compile @ 115-springboot-demo-smart-doc >>>
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ 115-springboot-demo-smart-doc ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 0 resource
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ 115-springboot-demo-smart-doc ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< smart-doc-maven-plugin:2.4.8:html (default-cli) < compile @ 115-springboot-demo-smart-doc <<<
[INFO] 
[INFO] 
[INFO] --- smart-doc-maven-plugin:2.4.8:html (default-cli) @ 115-springboot-demo-smart-doc ---
[INFO] ------------------------------------------------------------------------
[INFO] Smart-doc Start preparing sources at: 2022-07-01 22:43:54
[INFO] Artifacts that the current project depends on: ["org.springframework.boot:spring-boot-starter-web","org.springframework.boot:spring-boot-configuration-processor","org.projectlombok:lombok"]
[INFO] Smart-doc has loaded the source code path: [{"path":"D:/git/tech-anarkh-spring-demos/115-springboot-demo-smart-doc/src/main/java"}]
[INFO] Smart-doc Starting Create API Documentation at: 2022-07-01 22:43:54
[INFO] API documentation is output to => D://md2
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.196 s
[INFO] Finished at: 2022-07-01T22:43:55+08:00
[INFO] ------------------------------------------------------------------------

构建后的html如下:

也可以看到还自动提供了mock的数据,以及测试接口的按钮。还包含自定义的返回枚举类型等。

展示效果,可以参看https://api.doubans.com/在新窗口打开

生成更多类型的文档

smart-doc 还支持生成如下类型的文档:

bash
//生成markdown
mvn -Dfile.encoding=UTF-8 smart-doc:markdown
//生成adoc
mvn -Dfile.encoding=UTF-8 smart-doc:adoc
//生成postman json数据
mvn -Dfile.encoding=UTF-8 smart-doc:postman
// 生成 Open Api 3.0+, Since smart-doc-maven-plugin 1.1.5
mvn -Dfile.encoding=UTF-8 smart-doc:openapi

进一步理解

结合smart-doc官方文档,我们通过几个问题进一步理解smart-doc。主要内容来源于官方文档在新窗口打开

注释信息是有限的,smart-doc如何从注释拓展文档内容呢?

我们知道注释的信息是有限的,swagger技术栈的方式通过定义注解来约束并拓展文档中的内容,那么smart-doc如何从注释拓展文档内容呢?

一方面smart-doc的实现初衷是通过使用javadoc文档注释来去除注解式的侵入,因此smart-doc每增加一个功能首先都是去考虑javadoc原生的tag,

下面对smart-doc使用的一些javadoc的注释tag做介绍。

tag名称使用描述
@param对于在Spring Boot接口层,对于简单类型的参数必须在使用@param时写上注释描述,对于Entity类型smart-doc则不会检查
@deprecated可以在注释中用于标记接口已经废弃,作用同@Deprecated注解
@apiNote@apiNoteJAVA新增的文档tag,smart-doc使用@apiNote的注释作为方法的详细描述,因此可以使用@apiNote来写一段长注释。如果一个方法不写 @apiNote注释说明,smart-doc直接使用方法默认注释填充

另一方面,原生的tag是不够的,所以smart-doc又通过自定义tag来支持更多功能的拓展

tag名称描述
@ignore@ignore tag用于过滤请求参数对象上的某个字段,设置后smart-doc不输出改字段到请求参数列表中。关于响应字段忽略的请看【忽略响应字段】在新窗口打开 如果@ignore加到方法上,则接口方法不会输出到文档。从1.8.4开始@ignore支持添加到Controller上进行忽略不想生成文档的接口类。@ignore也可以用于方法上忽略某个请求参数。
@required如果你没有使用JSR303参数验证规范实现的方式来标注字段,就可以使用@required去标注请求参数对象的字段,标注smart-doc在输出参数列表时会设置为true
@mocksmart-doc 1.8.0开始,@mock tag用于在对象基本类型字段设置自定义文档展示值。设置值后smart-doc不再帮你生成随机值。方便可以通过smart-doc直接输出交付文档。
@dubbosmart-doc 1.8.7开始,@dubbo tag用于在DubboAPI接口类上添加让smart-doc可以扫描到Dubbo RPC的接口生成文档。
@restApismart-doc 1.8.8开始,@restApi tag用于支持smart-doc去扫描Spring Cloud Feign的定义接口生成文档。
@ordersmart-doc 1.9.4开始,@order tag用于设置Controller接口或者API入口的自定义排序序号,@order 1就表示设置序号为1
@ignoreResponseBodyAdvicesmart-doc 1.9.8开始,@ignoreResponseBodyAdvice tag用于忽略ResponseBodyAdvice设置的包装类。
@downloadsmart-doc 2.0.1开始,@download tag用于标注在Controller的文件下载方法上,生成debug页面时可实现文件下载测试。并且支持下载文件带请求头参数测试。
@pagesmart-doc 2.0.2开始,@page tag用于标注在Controller的方法上表示该方法用来渲染返回一个静态页面,生成debug页面时如果发起测试,测试页面会自动在浏览器开启新标签显示页面。
@ignoreParamssmart-doc 2.1.0开始,@ignoreParams tag用于标注在Controller方法上忽略掉不想显示在文档中的参数,例如:@ignoreParams id name,多个参数名用空格隔开
@responsesmart-doc 2.2.0开始,@response tag标注在Controller方法上可以允许用这自己定义返回的json example。建议只在返回基础类型时使用,如:Result&lt;String&gt;类型这种泛型是简单原生类型的响应。
@tag@since 2.2.5, @tag用于将Controller方法分类, 可以将不同Contoller下的方法指定到多个分类下, 同时也可以直接指定Controller为一个分类或多个分类

Maven多模块中使用插件有没有比较好的实践?

在独立的Maven项目中使用smart-doc,当前可以说是如丝般爽滑。但是在Maven的多模块项目中使用smart-doc-maven-plugin时,很多同学就有疑问了, smart-doc插件我到底是放在什么地方合适?是放在Maven的根pom.xml中?还是说各个需要生成API接口文档的模块中呢? 下面就来说说根据不同的项目结构应该怎么放插件。

完全的父子级关系的maven项目:

bash
├─parent
├──common
   pom.xml
├──web1
   pom.xml
├──web2
   pom.xml
└─pom.xml

上面的maven结构假设是严格按照父子级来配置的,然后web1和web2都依赖于common, 这种情况下如果跑到web1下或者web2目录下直接执行mvn命令来编译 都是无法完成的。需要在根目录上去执行命令编译命令才能通过,而smart-doc插件会通过类加载器去加载用户配置的一些类,因此是需要调用编译的和执行命令 是一样的。这种情况下建议你建smart-doc-maven-plugin放到根pom.xml中,在web1和web2中放置各自的smart-doc.json配置。 然后通过-pl去指定让smart-doc生成指定 模块的文档。操作命令如下:

bash
mvn smart-doc:markdown -Dfile.encoding=UTF-8  -pl :web1 -am

mvn smart-doc:markdown -Dfile.encoding=UTF-8  -pl :web2 -am

如果不是按照严格父子级构建的项目,还是以上面的结构例子来说。common模块放在类parent中,但是common的pom.xml并没有定义parent。 common模块也很少变更,很多公司内部可能就直接把common单独depoly上传到了公司的Nexus仓库中,这种情况下web1和web2虽然依赖于common, 但是web1和web2都可以在web1和web2目录下用命令编译,这种情况下直接将smart-doc-maven-plugin单独放到web1和web2中是可以做构建生成文档的。

多模块测试用例参考在新窗口打开

注意: 怎么去使用插件并没有固定的模式,最重要的是熟练Maven的一些列操作,然后根据自己的项目情况来调整。技巧娴熟就能应对自如。 对于插件的使用,从smart-doc-maven-plugin 1.2.0开始,插件是能够自动分析生成模块的依赖来加载必要的源码,并不会将所有模块的接口文档合并到一个文档中。

如果生成文档时遇到问题,该如何调试?

在使用smart-doc-maven-plugin插件来构建生成API文档的过程中可能会出现一些错误问题。官方文档中提供了调试的方案:

  1. 添加smart-doc依赖

因为smart-doc-maven-plugin最终是使用smart-doc来完成项目的源码分析和文档生成的, 通常情况下真正的调试的代码是smart-doc。但这个过程主要通过smart-doc-maven-plugin来排查。

xml
<dependency>
     <groupId>com.github.shalousun</groupId>
     <artifactId>smart-doc</artifactId>
     <version>[最新版本]</version>
     <scope>test</scope>
</dependency>

注意: 使用smart-doc的版本最好和插件依赖的smart-doc版本一致。

  1. 添加断点

添加断点如图所示

  1. Debug模式运行构建目标

maven插件在idea中运行debug非常简单,操作如下图。

这样就可以直接进入断点了。

提示: 上面是通过插件去作为入口调试smart-doc的源码,如果你想调试插件本身的源码执行过程,则将插件的依赖添加到项目依赖中,如下:

xml
<dependency>
    <groupId>com.github.shalousun</groupId>
    <artifactId>smart-doc-maven-plugin</artifactId>
    <version>【maven仓库最新版本】</version>
</dependency>

然后通过上面的类似步骤调试smart-doc-maven-plugin的源码

示例源码

https://github.com/realanarkh/tech-anarkh-spring-demos

https://smart-doc-group.github.io/