一、項目介紹與條件準備
1、項目使用框架/模塊介紹
- Swagger3:Swagger 是一個規範和完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務;在 SpringBoot 應用中集成 Swagger3 比老的 Swagger2 簡單多了,它提供了一個 Starter 組件;
- knife4j:Knife4j 的前身是 swagger-bootstrap-ui,前身 swagger-bootstrap-ui 是一個純 swagger-ui 的 ui 皮膚項目,可以美化 swagger-ui!
2、Swagger-UI 常用註解
- @Api 註解:用於修飾 Controller 類,生成 Controller 相關文檔信息;
- @ApiOperation 註解:用於修飾 Controller 類中的方法,生成接口方法相關文檔信息;
- @ApiParam 註解:用於修飾接口中的參數,生成接口參數相關文檔信息;
- @ApiModelProperty 註解:用於修飾實體類的屬性,當實體類是請求參數或返回結果時,直接生成相關文檔信息。
3、項目結構説明
├── config — 配置文件POJO
├── controller — 控制層(將請求通過 url 匹配,分配到不同的接收器/方法進行處理,然後返回結果)
├── service — 服務層接口
└── impl — 服務層實現
├── mapper — 數據訪問層,與數據庫交互為 service 提供接口
├── entity — 實體對象
├── converter — 實體對象轉換器
├── dto — 持久層需要的實體對象(用於服務層與持久層之間的數據傳輸對象)
└── vo — 視圖層需要的實體對象(用於服務層與視圖層之間的數據傳輸對象)
├── utils — 工具類
└── Application.java — 入口啓動類
二、項目搭建與構造
1、添加項目 maven 依賴
# 添加項目 maven 依賴,pom.xml 文件添加內容如下:
<!-- Swagger3 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Swagger UI -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<!--在引用時請在maven中央倉庫搜索3.X最新版本號-->
<version>3.0.3</version>
</dependency>
2、啓動類添加註解
# 啓動類添加 @EnableWebMvc 註解
package com.lizhengi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* @author lizhengi
*/
@SpringBootApplication
@EnableWebMvc
public class LizhengiSampleSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(LizhengiSampleSpringBootApplication.class, args);
}
}
3、添加 Swagger-UI 的配置類
# Swagger2 API 文檔的配置
package com.lizhengi.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author liziheng
* @version 1.0.0
* @description Swagger2 API 文檔的配置
* @date 2022-12-09 12:12 上午
**/
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//為當前包下controller生成API文檔
.apis(RequestHandlerSelectors.basePackage("com.lizhengi.controller"))
//為有@Api註解的Controller生成API文檔
// .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
//為有@ApiOperation註解的方法生成API文檔
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI演示")
.description("SwaggerUI演示")
.contact("lizhengi")
.version("1.0")
.build();
}
}
4、WebMvcConfig 防止 Whitelabel Error Page 問題
package com.lizhengi.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
* @author liziheng
* @version 1.0.0
* @description
* @date 2022-12-12 3:36 下午
**/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 下面定義了攔截器,會導致 spring.resources.static-locations 配置失效
registry.addResourceHandler("/**").addResourceLocations("classpath:/webapp/public/");
// 配置 knife4j 文檔資源的訪問路徑
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
5、controller 控制層添加 Swagger 註解
# Swagger3Config Swagger3 API 文檔的配置
package com.lizhengi.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author liziheng
* @version 1.0.0
* @description Swagger3 API 文檔的配置
* @date 2022-12-09 03:12 上午
**/
@Configuration
public class Swagger3Config {
/**
* API 文檔信息
*
* @return -
*/
@Bean
public Docket api() {
// 自動生成文檔接口:http://localhost:8080/v3/api-docs
// API接口文檔界面:http://localhost:8080/swagger-ui/index.html
// Swagger UI 界面:http://localhost:8080/doc.html
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.liziheng.demo.controller"))
.paths(PathSelectors.regex("/article/*.*|/api/v1/*.*|/*.*"))
.build();
}
private ApiInfo apiInfo() {
return new ApiInfo(
"Swagger API 文檔",
"後台管理系統相關的接口",
"v1",
"協議地址",
new Contact("li", "https://github.com/", "@163.com"),
"MIT License", "http://opensource.org/licenses/MIT",
Collections.emptyList());
}
/**
* 解決swagger在springboot2.7以後的空指針異常
*
* @return -
*/
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
assert field != null;
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
}
6、實體對象 VO 實現
# EventVO Event-事件 視圖層實體對象實現
package com.lizhengi.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author liziheng
* @version 1.0.0
* @description Event-事件 視圖層實體對象實現
* @date 2022-12-09 0:58 上午
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EventVO {
/**
* 事件名稱
*/
@ApiModelProperty("事件名稱")
private String eventName;
/**
* 事件類型
*/
@ApiModelProperty("事件類型")
private String eventType;
/**
* 事件發生時間
*/
@ApiModelProperty("事件發生時間")
private String eventDate;
/**
* 事件地點
*/
@ApiModelProperty("事件地點")
private String eventLocation;
/**
* 事件人物
*
* @see CharacterVO
*/
@ApiModelProperty("事件人物")
private List<CharacterVO> characters;
/**
* 事件描述
*/
@ApiModelProperty("事件描述")
private String eventDescription;
}
# CharacterVO Character-人物 視圖層實體對象實現
package com.lizhengi.entity.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liziheng
* @version 1.0.0
* @description Character-人物 視圖層實體對象實現
* @date 2022-12-09 00:59 上午
**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CharacterVO {
@ApiModelProperty("人物名稱")
private String name;
@ApiModelProperty("人物介紹")
private String profile;
@ApiModelProperty("人物登場時間(負數表示公元前)")
private Integer appearanceDate;
}
7、controller 控制層實現
# EventController Event-事件 Controller
package com.lizhengi.controller;
import com.lizhengi.entity.vo.EventVO;
import com.lizhengi.service.impl.EventServiceImpl;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author liziheng
* @version 1.0.0
* @description Event-事件 Controller
* @date 2022-12-07 8:17 下午
**/
@Api(tags = "EventController")
@RestController
@RequestMapping("/api/lizhengi/event")
public class EventController {
EventServiceImpl eventService;
@Autowired
public void setEventService(EventServiceImpl eventService) {
this.eventService = eventService;
}
@ApiOperation("全量獲取事件信息")
@RequestMapping(path = {"/list"}, method = RequestMethod.GET)
public List<EventVO> getEventDtoList() {
return eventService.getEventDtoList();
}
}
8、控制枱輸出 Swagger 接口文檔地址
# SwaggerPrintConfig 控制枱輸出 Swagger 接口文檔地址
package com.lizhengi.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.net.Inet4Address;
import java.net.UnknownHostException;
/**
* @author liziheng
* @version 1.0.0
* @description 控制枱輸出 Swagger 接口文檔地址
* @date 2022-12-09 3:28 上午
**/
@Component
@Slf4j
public class SwaggerPrintConfig implements ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
try {
// 獲取IP
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
// 獲取端口號
int port = event.getWebServer().getPort();
// 獲取應用名
String applicationName = event.getApplicationContext().getApplicationName();
// 打印 swagger 文檔地址
log.info("項目啓動啓動成功!swagger3 接口文檔地址: http://" + hostAddress + ":" + port + applicationName + "/swagger-ui/index.html");
// 打印 swagger2 文檔地址
log.info("項目啓動啓動成功!swaggerUI 接口文檔地址: http://" + hostAddress + ":" + port + applicationName + "/doc.html");
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}