apidoc-generator API文档生成器简介

一、概述

基于java源码中的注释(不限于多行或单行)和泛型解析,来生成api文档元数据和具体文档输出的工具

只需要指定想要解析的接口源码目录,并保证接口涉及的所有类都包含在classpath下,然后再设置渲染方式,最后运行便可获得最终结果

目前支持json-with-comment 和 swagger-editor两种输出方式

二、实现原理

@startuml
folder "数据源" {
   component [source源码] as src
   component [source源码对应的class和依赖] as classpath
}

package "Java源码解析" {
    component [Java源码解析 com.github.javaparser] as javaparser
    src --> javaparser
}

node "Java类型解析" {
    component [类型解析 databind-core] as type
    component [数据Mock databind-json] as mock
    component [带注释的Json序列化 ] as jwc
    classpath --> type
    type --> mock
    mock --> jwc
}

frame "文档渲染" {
    component [API Doc元数据] as doc
    component [Markdown] as md
    component [Swagger元数据] as sg

    javaparser --> doc
    type --> doc
    doc --> mock

    jwc --> md
    doc --> md

    doc --> sg
}

@enduml

三、接入姿势

3.1 项目侵入式接入

可以编写单元测试或单独的模块来接入

3.1.1 gradle 依赖引入

repositories {
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

testImplementation 'org.dreamcat:apidoc-generator:0.1-SNAPSHOT'

3.1.2 maven 依赖引入

<project>
  <repository>
    <id>snapshots-repo</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    <releases><enabled>false</enabled></releases>
    <snapshots><enabled>true</enabled></snapshots>
  </repository>

  <dependency>
    <groupId>org.dreamcat</groupId>
    <artifactId>apidoc-generator</artifactId>
    <version>0.1-SNAPSHOT</version>
    <scope>test</scope>
  </dependency>
</project>

3.1.3 单元测试示例

源码样例

public interface ComplexService {

    // list complex
    ApiResult<ApiPageSummary<ComplexModel, ComplexSummaryModel>> list(ComplexListParam param);

    /**
     * create complex
     *
     * @param param require param to create complex
     * @param file attachment
     * @return complex id
     */
    ApiResult<String> create(ComplexCreateParam param, byte[] file);
}
public class ComplexListParam extends PageParam {

    private String token; // the token to sign
    private Set<Integer> userIds; // users who admire the number
}

单元测试

import java.util.Collections;
import org.dreamcat.cli.generator.apidoc.ApiDocConfig;
import org.dreamcat.cli.generator.apidoc.ApiDocGenerator;
import org.dreamcat.cli.generator.apidoc.renderer.JsonWithCommentRenderer;
import org.junit.jupiter.api.Test;

class JavaDocTest {

    @Test
    void test() throws Exception {
        String srcDir = "/path/to/project/src/main/java";
        // 想要分析的Service接口的源码目录
        String javaFileDir = srcDir + "/com/example/service";
        // 支持源码分析的java基础包名
        String basePackage = "com.example";

        // API文档生成器的配置
        ApiDocConfig config = new ApiDocConfig();
        config.setBasePackages(Collections.singletonList(basePackage));
        config.setSrcDirs(Collections.singletonList(srcDir));
        config.setJavaFileDirs(Collections.singletonList(javaFileDir));
        
        // 第一种输出:设置渲染方式为带注释的Json markdown格式
        JsonWithCommentRenderer jsonWithCommentRenderer = new JsonWithCommentRenderer(config);
        jsonWithCommentRenderer.setSeqFormatter(i -> "#### 4.1." + i);
        
        ApiDocGenerator<String> markdownGenerator = new ApiDocGenerator<>(config, jsonWithCommentRenderer);
        String markdown = markdownGenerator.generate();
        System.out.println(markdown);
        
        // 第二种输出:设置渲染方式为swagger-editor yaml格式
        SwaggerRenderer swaggerRenderer = new SwaggerRenderer();
        swaggerRenderer.setdDfaultTitle("Some Stuff");

        ApiDocGenerator<String> yamlGenerator = new ApiDocGenerator<>(config, swaggerRenderer);
        String yaml = yamlGenerator.generate();
        System.out.println(yaml);
    }
}

四、输出姿势

4.1 带注释的Json Markdown文档输出

示例如下

4.1.1 list complex

入参

{
    "pageNo": 1616467239, //  page number, default is 1
    "pageSize": 1187568143, //  page size, default is 10
    "token": "05Ie60000000", //  the token to sign
    "userIds": [
        1360046618
    ] //  users who admire the number
}

出参

{
    "code": "Z14dx00", //  response code
    "msg": "6d1B", //  error message
    "data": {
        "pageNo": 332708021, //  page number, default is 1
        "pageSize": 1363282705, //  page size, default is 10
        "list": [
            {
                "id": "0HN5X", // /! entity id
                "createdBy": 7819338319132650901, //     who created the record    
                "createdAt": "2021-12-20 20:18:56", // 
                "a": 2114287267, //  a or a + bi
                "b": 1421927723, //  b of a + bi
                "salt": [
                    3
                ], //  salt to sign 
                "admired": [
                    {
                        "id": 6733097960264216561,
                        "name": "2O9eO000000000",
                        "gender": female
                    }
                ] //      * the people who admire the number     
            }
        ], //  the list data per page
        "totalCount": 1593462666, //  the total count
        "totalPage": 585256653, //  the total page
        "summary": {
            "todayCount": 940383353038686031, //  count of today
            "last7dayCount": 2958055079033281580, //  count of last 7 day
            "bits": [
                367530979
            ] //  magic bits
        } //  the summary per page
    } //  real data
}

4.1.2 create complex

入参

{
    "a": 15466019106679717788876966621497800342, //  some of complex
    "b": 1.14164135352093225954791023656452E+616, //  some of complex
    "props": {
        "Q14iG0000": "VWsEa00000"
    } //  props for extra settings 
}

出参

{
    "code": "k", //  response code
    "msg": "7RwO600", //  error message
    "data": "OgF6" //  real data
}

4.2 Swagger Editor Yaml文档输出

示例如下

---
swagger: "2.0"
info:
  version: "1.0.0"
  title: "Default Title"
tags:
- name: "com.example.controller.ComplexController"
  description: ""
schemes:
- "https"
- "http"
paths:
  /api/v1/get:
    get:
      tags:
      - "com.example.controller.ComplexController"
      description: "get complex"
      operationId: "GET_get"
      parameters:
      - in: "query"
        name: "id"
        description: ""
        required: true
        type: "string"
      responses:
        "200":
          description: "ApiResult<ComplexModel>"
          schema:
            $ref: "#/definitions/ApiResult_ComplexModel"
definitions:
  ApiResult_ComplexModel:
    type: "object"
    properties:
      msg:
        type: "string"
      data:
        type: "object"
        properties:
          admired:
            type: "array"
            items:
              type: "object"
              properties:
                name:
                  type: "string"
                gender:
                  type: "string"
                  enum:
                  - "unknown"
                  - "female"
                  - "male"
                id:
                  type: "object"
                  format: "int64"
          createdAt:
            type: "object"
            format: "date-time"
          a:
            type: "object"
            format: "int32"
          b:
            type: "integer"
            format: "int32"
          salt:
            type: "array"
            items:
              type: "object"
          createdBy:
            type: "integer"
            format: "int64"
          id:
            type: "string"
      code:
        type: "string"

Python - CP01E01

python

写于 2018-10-17 的一篇python教程

a python file like this:

#!/usr/bin/env python3
#coding=utf-8

# it will print a poetry for python
import this

if __name__ == '__main__':
    print('Hello, World!')

at line 1: it will search a file named python3 in your $PATH to execute

at line 2: #coding=utf-8 or # -*- coding: UTF-8 -*-, will make python support your local characters.

at line 8: in python 2 version, it can replace with print 'Hello, World!' . In fact, print is a function in pyhton3 but a command in python2.

You can save this to a file such as awesome.py , and type the command in terminal: /usr/bin/env python3 awesome.py.

Declaration and assignment

a = b = c = 1
assert a == 1 and b == c and a + b + c == 3

a, b, c = 1, 3.14, "a simple string"

Operator

import random

assert 1 + 1 - 1 * 1 + 1 / 1 == 2.0
# <float> = <int> / <int>
assert type(1 / 3) == float
assert 1 / 3 > 0.3333 and 1 / 3 < 0.3334
assert 6 / 10 == 3 / 5 

# <int> = <int> // <int>
assert 16 // 3 == 5 and 16 // 4 == 4 and 16 // 5 == 3
# <int> = <int> % <int>
assert 16 % 3 == 1 and 16 % 4 == 0 and 16 % 5 == 1

# 16 - 20
a = random.randint(16, 21)
# 2 - 7
b = random.randint(2, 8)
print("a = %d, b = %d" % (a, b))
assert a == b * (a // b) + (a % b)

# <number> ** <number>
assert 2 ** 4 == 16 and 2 ** 8 == 256
assert 2 ** 16 == 65536
assert 1.2 ** 2 == 1.44

Data type

#!/usr/bin/python
# -*- coding: UTF-8 -*-

# integer
ten_i = 10
# float
one_tenth_f = 1.0E-1
# string
hello_world = 'Hello' + " " + """
world!
"""

assert type(ten) == int
assert type(one_tenth_f) == float
assert type(hello_world) == str

Data type: list, tuple and dict

beatles = ["John", "Paul", "George", "Lingo"]

if '''Bob''' in beatles:
    print("You just kiding me!")
elif """Lennon""" in beatles:
    print('Right, but never print this!')
else:
    pass

for name in beatles:
    print("The member of the beatles: %s" % name)
  
assert beatles[0] == "John"
# change the member's value in the list
beatles[0] = "John Lennon"
assert beatles[0] == "John Lennon"