使用 JSONDoc 記錄 Spring Boot RESTful API

這個博文可以分爲兩部分:第一部分我將編寫一個Spring Boot RESTful API,第二部分將介紹如何使用JSONDoc來記錄創建的API。做這兩個部分最多需要15分鐘,因爲使用Spring Boot創建一個API非常簡單快捷,並且使用JSONDoc Spring Boot啓動器和UI webjar進行記錄也是如此。我將跳過這個例子的測試創建,因爲主要目標是如何記錄API而不是編寫和測試它。

編寫API

我們首先根據快速入門的原型創建Maven項目



並聲明API所需的依賴關係:

  • spring-boot-starter-web
  • spring-boot-starter-data-jpa
  • h2
我還添加了 Lombok 保持我的代碼更乾淨。所得的pom看起來像這樣:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>org.example</groupId>
	<artifactId>jsondoc-shelf</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>jsondoc-shelf</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
	
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>1.2.0.RELEASE</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
			<version>1.2.0.RELEASE</version>
		</dependency>
		
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<version>1.3.176</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.14.8</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		
	</dependencies>
</project>
這個應用程序將是一個管理簡單貨架的服務的集合。將有兩個實體:
  • Book
  • Author

創建 Entities 和 Controllers

爲此,我將創建通常的組件來管理持久層和控制器層:

  • 一個包名爲model將包含BookAuthor
  • 一個包名爲repository將包含BookRepositoryAuthorRepository
  • 一個包名爲controller將包含BookControllerAuthorController

對於這個例子,我將跳過 Service 層。我還將創建一個DatabasePopulator類,實現CommandLineRunner,以便在啓動時將在內存數據庫中存在一些數據。我們來看看實體,存儲庫和控制器的代碼:

Entities
package org.example.shelf.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Entity
@Data
@EqualsAndHashCode(exclude = "id")
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Column(name = "title")
	private String title;

	@ManyToOne
	@JoinColumn(name = "author_id")
	private Author author;

}
package org.example.shelf.model;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import com.fasterxml.jackson.annotation.JsonIgnore;

@Entity
@Data
@NoArgsConstructor
@ToString(exclude = "books")
@EqualsAndHashCode(of = "name")
public class Author {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;

	@Column(name = "name")
	private String name;

	@JsonIgnore
	@OneToMany(mappedBy = "author", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	private List<Book> books = new ArrayList<Book>();
	
}
Repositories
package org.example.shelf.repository;

import org.example.shelf.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookRepository extends JpaRepository<Book, Long> {

}
package org.example.shelf.repository;

import org.example.shelf.model.Author;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

public interface AuthorRepository extends JpaRepository<Author, Long> {

}
Controllers
package org.example.shelf.controller;

import java.util.List;

import org.example.shelf.flow.ShelfFlowConstants;
import org.example.shelf.model.Book;
import org.example.shelf.repository.BookRepository;
import org.jsondoc.core.annotation.Api;
import org.jsondoc.core.annotation.ApiBodyObject;
import org.jsondoc.core.annotation.ApiMethod;
import org.jsondoc.core.annotation.ApiPathParam;
import org.jsondoc.core.annotation.ApiResponseObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

@RestController
@RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
public class BookController {
	
	@Autowired
	private BookRepository bookRepository;
	
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public Book findOne(@PathVariable Long id) {
		return bookRepository.findOne(id);
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public List<Book> findAll() {
		return bookRepository.findAll();
	}
	
	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	@ResponseStatus(value = HttpStatus.CREATED)
	public ResponseEntity<Void> save(@RequestBody Book book, UriComponentsBuilder uriComponentsBuilder) {
		bookRepository.save(book);
		
		HttpHeaders headers = new HttpHeaders();
		headers.setLocation(uriComponentsBuilder.path("/books/{id}").buildAndExpand(book.getId()).toUri());
		return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	
	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	@ResponseStatus(value = HttpStatus.OK)
	public void delete(@PathVariable Long id) {
		Book book = bookRepository.findOne(id);
		bookRepository.delete(book);
	}

}
package org.example.shelf.controller;

import java.util.List;

import org.example.shelf.flow.ShelfFlowConstants;
import org.example.shelf.model.Author;
import org.example.shelf.repository.AuthorRepository;
import org.jsondoc.core.annotation.Api;
import org.jsondoc.core.annotation.ApiBodyObject;
import org.jsondoc.core.annotation.ApiMethod;
import org.jsondoc.core.annotation.ApiPathParam;
import org.jsondoc.core.annotation.ApiResponseObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

@RestController
@RequestMapping(value = "/authors", produces = MediaType.APPLICATION_JSON_VALUE)
public class AuthorController {

	@Autowired
	private AuthorRepository authorRepository;

	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public Author findOne(@PathVariable Long id) {
		return authorRepository.findOne(id);
	}

	@RequestMapping(method = RequestMethod.GET)
	public List<Author> findAll() {
		return authorRepository.findAll();
	}

	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	@ResponseStatus(value = HttpStatus.CREATED)
	public ResponseEntity<Void> save(@RequestBody Author author, UriComponentsBuilder uriComponentsBuilder) {
		authorRepository.save(author);
		
		HttpHeaders headers = new HttpHeaders();
		headers.setLocation(uriComponentsBuilder.path("/authors/{id}").buildAndExpand(author.getId()).toUri());
		return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}

	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	@ResponseStatus(value = HttpStatus.OK)
	public void delete(@PathVariable Long id) {
		Author author = authorRepository.findOne(id);
		authorRepository.delete(author);
	}

}
Database populator
package org.example.shelf;

import org.example.shelf.model.Author;
import org.example.shelf.model.Book;
import org.example.shelf.repository.AuthorRepository;
import org.example.shelf.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DatabasePopulator implements CommandLineRunner {
	
	@Autowired
	private AuthorRepository authorRepository;
	
	@Autowired
	private BookRepository bookRepository;
	
	public void run(String... arg0) throws Exception {
		Author horbny = new Author();
		horbny.setId(1L);
		horbny.setName("Nick Horby");
		
		Author smith = new Author();
		smith.setId(2L);
		smith.setName("Wilbur Smith");
		
		authorRepository.save(horbny);
		authorRepository.save(smith);
		
		Book highFidelty = new Book();
		highFidelty.setId(1L);
		highFidelty.setTitle("High fidelty");
		highFidelty.setAuthor(horbny);
		
		Book aLongWayDown = new Book();
		aLongWayDown.setId(2L);
		aLongWayDown.setTitle("A long way down");
		aLongWayDown.setAuthor(horbny);

		Book desertGod = new Book();
		desertGod.setId(3L);
		desertGod.setTitle("Desert god");
		desertGod.setAuthor(smith);
		
		bookRepository.save(highFidelty);
		bookRepository.save(aLongWayDown);
		bookRepository.save(desertGod);
	}

}
現在是編寫主類來運行應用程序的時候了。Shelf在這種情況下,我會稱之爲Spring Boot,這很簡單:

package org.example.shelf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAutoConfiguration
@EnableJpaRepositories
@ComponentScan
public class Shelf {
	
	public static void main(String[] args) {
		SpringApplication.run(Shelf.class, args);
	}

}
通過運行這個類,我們可以實際驗證應用程序是否響應請求。您可以通過使用 curl 輕鬆測試 API 的工作:

curl -i http://localhost:8080/books/1
curl -i http://localhost:8080/books

curl -i http://localhost:8080/authors/1
curl -i http://localhost:8080/authors

用JSONDoc記錄API

這是有趣的和新的部分,即使用JSONDoc庫來註釋代碼並自動生成其文檔。要做到這一點,你必須聲明JSONDoc依賴關係,並在你的類中插入一些代碼。讓我們看看如何做到這一點:

聲明JSONDoc依賴關係

只需添加兩個依賴關係到pom文件:

<dependency>
	<groupId>org.jsondoc</groupId>
	<artifactId>spring-boot-starter-jsondoc</artifactId>
	<version>1.1.3</version>
</dependency>

<dependency>
	<groupId>org.jsondoc</groupId>
	<artifactId>jsondoc-ui-webjar</artifactId>
	<version>1.1.3</version>
</dependency>

在主類中啓用JSONDoc

使用JSONDoc啓動器,您可以通過添加@EnableJSONDocShelf啓用文檔生成,如下所示:

package org.example.shelf;

import org.jsondoc.spring.boot.starter.EnableJSONDoc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAutoConfiguration
@EnableJpaRepositories
@EnableJSONDoc
@ComponentScan
public class Shelf {
	
	public static void main(String[] args) {
		SpringApplication.run(Shelf.class, args);
	}

}

配置JSONDoc

接下來要做的是配置JSONDoc來掃描您的控制器,對象和流類。要做到這一點,只需添加一些條目到application.properties文件(src/main/resources如果你沒有它創建它)

jsondoc.version=1.0
jsondoc.basePath=http://localhost:8080
jsondoc.packages[0]=org.example.shelf.model
jsondoc.packages[1]=org.example.shelf.controller

文檔控制器

JSONDoc可以從Spring註釋中獲取幾個信息來構建文檔。無論如何,它是一個選擇加入的過程,這意味着JSONDoc將僅在使用自己的註釋註釋時才掃描類和方法。例如,要正確記錄BookController,這裏是如何使用JSONDoc註釋:

package org.example.shelf.controller;

import java.util.List;

import org.example.shelf.flow.ShelfFlowConstants;
import org.example.shelf.model.Book;
import org.example.shelf.repository.BookRepository;
import org.jsondoc.core.annotation.Api;
import org.jsondoc.core.annotation.ApiBodyObject;
import org.jsondoc.core.annotation.ApiMethod;
import org.jsondoc.core.annotation.ApiPathParam;
import org.jsondoc.core.annotation.ApiResponseObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

@RestController
@RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(description = "The books controller", name = "Books services")
public class BookController {
	
	@Autowired
	private BookRepository bookRepository;
	
	@ApiMethod
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public @ApiResponseObject Book findOne(@ApiPathParam(name = "id") @PathVariable Long id) {
		return bookRepository.findOne(id);
	}
	
	@ApiMethod
	@RequestMapping(method = RequestMethod.GET)
	public @ApiResponseObject List<Book> findAll() {
		return bookRepository.findAll();
	}
	
	@ApiMethod
	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	@ResponseStatus(value = HttpStatus.CREATED)
	public @ApiResponseObject ResponseEntity<Void> save(@ApiBodyObject @RequestBody Book book, UriComponentsBuilder uriComponentsBuilder) {
		bookRepository.save(book);
		
		HttpHeaders headers = new HttpHeaders();
	    headers.setLocation(uriComponentsBuilder.path("/books/{id}").buildAndExpand(book.getId()).toUri());
		return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	
	@ApiMethod
	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	@ResponseStatus(value = HttpStatus.OK)
	public void delete(@ApiPathParam(name = "id") @PathVariable Long id) {
		Book book = bookRepository.findOne(id);
		bookRepository.delete(book);
	}

}
同樣的AuthorController

文件對象

接下來要做的就是把一些JSONDoc註釋也需要被記錄在案,在這種情況下,對象BookAuthor這是Book類:

package org.example.shelf.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import org.jsondoc.core.annotation.ApiObject;
import org.jsondoc.core.annotation.ApiObjectField;

import lombok.Data;
import lombok.EqualsAndHashCode;

@Entity
@Data
@EqualsAndHashCode(exclude = "id")
@ApiObject
public class Book {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@ApiObjectField(description = "The book's ID")
	private Long id;

	@Column(name = "title")
	@ApiObjectField(description = "The book's title")
	private String title;

	@ManyToOne
	@JoinColumn(name = "author_id")
	@ApiObjectField(description = "The book's author")
	private Author author;

}
而且在這種情況下Author也是如此

檢查點:啓動應用程序

在開始記錄流程之前,讓我們啓動應用程序,看看會發生什麼:

  • 如果你去http://localhost:8080/jsondoc你會看到一個json,這是由JSONDoc生成的,它代表了基於控制器方法和模型對象上的註釋的文檔
  • 如果你去http://localhost:8080/jsondoc-ui.html你會看到JSONDoc UI。只需複製並粘貼http://localhost:8080/jsondoc到輸入字段中,並在清晰的用戶界面中獲取文檔

這是一個很好的時機,需要一些時間來探索界面,並在界面上玩API。

文件流

按照流程我的意思是一些API方法的後續執行,旨在實現一個目標,即可以購買一本書,或瀏覽目錄並獲取圖書詳細信息。在這種情況下,流程可能涉及幾種方法,API用戶可能需要知道哪個是正確的調用方法序列來實現目標。在這個例子中,我不能想到有意義的流程,但是讓我們假設我想要記錄瀏覽框架的方法順序,並通過我選擇的一本書獲取作者的細節,所以這個用例的結果流是就像是:

  • 獲取書籍清單
  • 選擇一本書並獲得其細節
  • 得到這本書的作者

要記錄此流程,您只需按照以下步驟操作:

  1. 創建一個包含應用程序流的類。此類僅用於文檔目的,不會在您的應用程序中實際使用。使用註釋來註釋這個類@ApiFlowSet,這使得JSONDoc瞭解在構建文檔時應該考慮到這個類。
  2. 在這個類中創建假的方法,註釋爲@ApiFlow方法的正文以及它的返回類型和參數可以是void,因爲方法簽名服務器只是作爲@ApiFlow註釋的鉤子
  3. 決定標識JSONDoc產生文檔內的每一個API方法中,例如一個ID的findAll方法的BookController可有一個像IDBOOK_FIND_ALL
  4. 將這個ID內部ID@ApiMethod註釋和內部 api methodid @ApiFlowStep註解
  5. 如果將流類放在一個單獨的包中,請記住application.properties使用該值更新該文件

我們來看看我是怎麼做到的 這是持有應用程序流程的類:

package org.example.shelf.flow;

import org.jsondoc.core.annotation.ApiFlow;
import org.jsondoc.core.annotation.ApiFlowSet;
import org.jsondoc.core.annotation.ApiFlowStep;

@ApiFlowSet
public class ShelfFlows {

	@ApiFlow(
		name = "Author detail flow",
		description = "Gets an author's details starting from the book's list",
		steps = {
			@ApiFlowStep(apimethodid = ShelfFlowConstants.BOOK_FIND_ALL),	
			@ApiFlowStep(apimethodid = ShelfFlowConstants.BOOK_FIND_ONE),	
			@ApiFlowStep(apimethodid = ShelfFlowConstants.AUTHOR_FIND_ONE)	
		}
	)
	public void authorDetailFlow() {

	}

}
這是包含註釋中要引用的方法ID的類:

package org.example.shelf.flow;

public class ShelfFlowConstants {
	
	// Book IDs
	public final static String BOOK_FIND_ALL = "BOOK_FIND_ALL";
	public final static String BOOK_FIND_ONE = "BOOK_FIND_ONE";
	public final static String BOOK_SAVE = "BOOK_SAVE";
	public final static String BOOK_DELETE = "BOOK_DELETE";

	// Author IDs
	public final static String AUTHOR_FIND_ALL = "AUTHOR_FIND_ALL";
	public final static String AUTHOR_FIND_ONE = "AUTHOR_FIND_ONE";
	public final static String AUTHOR_SAVE = "AUTHOR_SAVE";
	public final static String AUTHOR_DELETE = "AUTHOR_DELETE";

}
這是BookController,指定了id屬性後:

package org.example.shelf.controller;

import java.util.List;

import org.example.shelf.flow.ShelfFlowConstants;
import org.example.shelf.model.Book;
import org.example.shelf.repository.BookRepository;
import org.jsondoc.core.annotation.Api;
import org.jsondoc.core.annotation.ApiBodyObject;
import org.jsondoc.core.annotation.ApiMethod;
import org.jsondoc.core.annotation.ApiPathParam;
import org.jsondoc.core.annotation.ApiResponseObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;

@RestController
@RequestMapping(value = "/books", produces = MediaType.APPLICATION_JSON_VALUE)
@Api(description = "The books controller", name = "Books services")
public class BookController {
	
	@Autowired
	private BookRepository bookRepository;
	
	@ApiMethod(id = ShelfFlowConstants.BOOK_FIND_ONE)
	@RequestMapping(value = "/{id}", method = RequestMethod.GET)
	public @ApiResponseObject Book findOne(@ApiPathParam(name = "id") @PathVariable Long id) {
		return bookRepository.findOne(id);
	}
	
	@ApiMethod(id = ShelfFlowConstants.BOOK_FIND_ALL)
	@RequestMapping(method = RequestMethod.GET)
	public @ApiResponseObject List<Book> findAll() {
		return bookRepository.findAll();
	}
	
	@ApiMethod(id = ShelfFlowConstants.BOOK_SAVE)
	@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	@ResponseStatus(value = HttpStatus.CREATED)
	public @ApiResponseObject ResponseEntity<Void> save(@ApiBodyObject @RequestBody Book book, UriComponentsBuilder uriComponentsBuilder) {
		bookRepository.save(book);
		
		HttpHeaders headers = new HttpHeaders();
	    headers.setLocation(uriComponentsBuilder.path("/books/{id}").buildAndExpand(book.getId()).toUri());
		return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	
	@ApiMethod(id = ShelfFlowConstants.BOOK_DELETE)
	@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
	@ResponseStatus(value = HttpStatus.OK)
	public void delete(@ApiPathParam(name = "id") @PathVariable Long id) {
		Book book = bookRepository.findOne(id);
		bookRepository.delete(book);
	}

}
最後的application.properties文件,用新的包:

jsondoc.version=1.0
jsondoc.basePath=http://localhost:8080
jsondoc.packages[0]=org.example.shelf.model
jsondoc.packages[1]=org.example.shelf.controller
jsondoc.packages[2]=org.example.shelf.flow
現在是再次啓動應用程序的時候,轉到http://localhost:8080/jsondoc-ui.html,插入http://localhost:8080/jsondoc輸入框並獲取文檔。請享用!


資源

這是項目的結構:


鏈接

關注公衆號,分享乾貨,討論技術


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章