SpringBoot2.x 集成 Swagger2

本文详细介绍了如何在SpringBoot2.4.5项目中集成Swagger2,并通过SpringFox实现分组管理,同时引入Knife4j增强Swagger的UI展示。内容涵盖依赖引入、配置类编写、实体类与Controller层的注解使用,以及异常处理和接口测试。此外,还展示了如何使用Knife4j进行文档展示和接口测试。
摘要由CSDN通过智能技术生成

Swagger是一款RESTFUL接口的文档在线自动生成加功能测试的软件,提供描述、生产、消费和可视化RESTful Web Service。Swagger也是一个api文档维护组织,后来成为了OpenAPI(一个业界的api文档标准)标准的主要定义者,现在最新的版本为17年发布的Swagger3(OpenAPI3)。本文所用的Swagger2基于OpenAPI2,于2017年停止维护。

SpringFox是Spring社区维护的一个项目(非官方),帮助使用者将Swagger2集成到Spring中。常用于Spring中帮助开发者生成文档,并可以轻松的在SpringBoot中使用,目前已经支持OpenAPI3标准。本文通过引入SpringFox来使用Swagger2。

本文主要对SpringBoot2.x集成Swagger2进行简单总结,其中SpringBoot使用的2.4.5版本。

一、引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.10.5</version>
    <exclusions>
        <exclusion>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
        </exclusion>
        <exclusion>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.10.5</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-spring-webmvc</artifactId>
    <version>2.10.5</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-annotations</artifactId>
    <version>1.5.21</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swagger-models</artifactId>
    <version>1.5.21</version>
</dependency>
<!-- lombok插件 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

二、编写配置类

package com.rtxtitanv.config;

import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.config.Swagger2Config
 * @description Swagger2配置类
 * @date 2021/6/6 15:16
 */
@Configuration
@EnableKnife4j
@EnableSwagger2WebMvc
public class Swagger2Config {

    /**
     * 配置默认分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket defaultApi() {
        // DocumentationType.SWAGGER_2为Swagger2的文档类型
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
            /*
             * 通过apis和paths方法设置ApiSelector的构建规则
             * ApiSelectorBuilder apis(Predicate<RequestHandler> selector)
             *     RequestHandlerSelectors.any():构建所有API
             *     RequestHandlerSelectors.none():所有API都不构建
             *     RequestHandlerSelectors.basePackage():构建指定包路径下的所有API
             *     RequestHandlerSelectors.withClassAnnotation():仅构建带有指定类注解的API
             *     RequestHandlerSelectors.withMethodAnnotation():仅构建带有指定方法注解的API
             * ApiSelectorBuilder paths(Predicate<String> selector)
             *     PathSelectors.any():构建所有请求路径的API
             *     PathSelectors.none():所有请求路径的API都不构建
             *     PathSelectors.regex():仅构建正则匹配的请求路径的API
             *     PathSelectors.ant():仅构建与ant模式匹配的API
             */
            .apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.other")).paths(PathSelectors.any())
            .build();
    }

    /**
     * 配置user分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket userApi() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
            .apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.user"))
            // groupName方法设置分组名称,globalOperationParameters方法添加全局参数
            .paths(PathSelectors.regex("/user/.*")).build().groupName("user").globalOperationParameters(token())
            .ignoredParameterTypes(HttpServletRequest.class, HttpServletResponse.class);
    }

    /**
     * 配置order分组
     *
     * @return springfox.documentation.spring.web.plugins.Docket
     */
    @Bean
    public Docket orderApi() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
            .apis(RequestHandlerSelectors.basePackage("com.rtxtitanv.controller.order"))
            .paths(PathSelectors.regex("/order/.*")).build().groupName("order").globalOperationParameters(token())
            .ignoredParameterTypes(HttpServletRequest.class, HttpServletResponse.class);
    }

    /**
     * 构建API文档基本信息
     *
     * @return springfox.documentation.service.ApiInfo
     */
    private ApiInfo apiInfo() {
        // 联系人信息:分别为作者、主页、邮箱
        Contact contact = new Contact("RtxTitanV", "https://blog.csdn.net/RtxTitanV", "RtxTitanV@xxx.com");
        // 构建API文档的基本信息:依次为API标题、API描述、API联系人信息、API版本、API许可、API许可Url
        return new ApiInfoBuilder().title("SpringBoot2.x 集成 Swagger2").description("SpringBoot2.x 集成 Swagger2 测试文档")
            .contact(contact).version("1.0.0").license("Apache 2.0")
            .licenseUrl("https://www.apache.org/licenses/LICENSE-2.0").build();
    }

    /**
     * 配置JWT,在全局参数加入Authorization请求头
     *
     * @return List<Parameter>
     */
    private List<Parameter> token() {
        ParameterBuilder tokenPar = new ParameterBuilder();
        ArrayList<Parameter> pars = new ArrayList<>();
        tokenPar.name("Authorization").description("token").modelRef(new ModelRef("string")).parameterType("header")
            .defaultValue("Bearer ").required(true).build();
        pars.add(tokenPar.build());
        return pars;
    }
}

配置类中每一个Docket对象都代表一个分组,Bean的名称不能相同,通过调用groupName(String groupName)方法来设置分组名。以上配置了三个分组,分别为default、user、order三组,其中没有调用groupName(String groupName)的Docket对象是默认分组配置,不同的分组可以配置不同的API文档基本信息(文档标题、描述、联系人信息等),这里主要是测试,就都配置的统一的API文档基本信息。另外user和order分组都配置了JWT认证,请求时在请求头需携带一个token。

Doket类的方法Docket enable(boolean externallyConfiguredFlag)可以控制Swagger的开启和关闭,可以根据不同环境进行动态的配置。

三、创建实体类

package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.User
 * @description 用户实体类
 * @date 2021/6/6 15:54
 */
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "User", description = "用户信息")
public class User implements Serializable {

    private static final long serialVersionUID = -1731585069930254532L;
    @ApiModelProperty(value = "用户id")
    private Long id;
    @ApiModelProperty(value = "用户名")
    private String username;
    @ApiModelProperty(value = "密码")
    private String password;
}
package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.Order
 * @description 订单实体类
 * @date 2021/6/8 11:30
 */
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "Order", description = "订单信息")
public class Order implements Serializable {

    private static final long serialVersionUID = -688022729580581140L;
    @ApiModelProperty(value = "订单id")
    private Long id;
    @ApiModelProperty(value = "订单编号")
    private String orderNumber;
    @ApiModelProperty(value = "订单描述")
    private String orderDescription;
    @ApiModelProperty(value = "订单所属用户id")
    private Long userId;
}
package com.rtxtitanv.model;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.CommonResult
 * @description 通用响应类
 * @date 2021/6/8 10:46
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
@ApiModel(value = "CommonResult", description = "通用响应对象")
public class CommonResult<T> implements Serializable {

    private static final long serialVersionUID = 1319649232291761032L;
    @ApiModelProperty(value = "响应码", required = true)
    private int code;
    @ApiModelProperty(value = "响应信息", required = true)
    private String message;
    @ApiModelProperty(value = "响应数据")
    private T data;

    public static <T> CommonResult<T> success(String message, T data) {
        return new CommonResult<>(HttpStatus.OK.value(), message, data);
    }

    public static <T> CommonResult<T> invalidParameter(String message) {
        return new CommonResult<>(HttpStatus.BAD_REQUEST.value(), message, null);
    }

    public static <T> CommonResult<T> notFound(String message) {
        return new CommonResult<>(HttpStatus.NOT_FOUND.value(), message, null);
    }
}

四、Controller层

分组default中的用于简单测试的Controller:

package com.rtxtitanv.controller.other;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.other.ApiController
 * @description HelloController
 * @date 2021/6/8 17:03
 */
@Api(value = "HelloController", tags = "Hello World", description = "用于简单测试的API")
@RequestMapping("/hello")
@RestController
public class HelloController {

    @ApiOperation(value = "hello", notes = "一个简单的测试接口")
    @ApiImplicitParam(value = "名称", name = "name", required = true, defaultValue = "world")
    @GetMapping("/world/{name}")
    public String hello(@PathVariable(value = "name") String name) {
        return "hello " + name;
    }
}
package com.rtxtitanv.controller.other;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.other.TestController
 * @description TestController
 * @date 2021/6/6 19:33
 */
@Api(value = "TestController", tags = "简单测试", description = "用于简单测试的API")
@RequestMapping("/test")
@RestController
public class TestController {

    @ApiOperation(value = "hello", notes = "一个简单的测试接口")
    @ApiImplicitParam(value = "名称", name = "name", required = true, defaultValue = "world")
    @GetMapping("/hello/{name}")
    public String hello(@PathVariable(value = "name") String name) {
        return "hello " + name;
    }
}

分组user中的Controller:

package com.rtxtitanv.controller.user;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import com.rtxtitanv.service.UserService;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.user.UserController
 * @description UserController
 * @date 2021/6/6 15:52
 */
@Api(value = "用户模块", tags = "用户管理", description = "用户的增删改查", protocols = "http")
@RequestMapping("/user")
@RestController
public class UserController {

    @Resource
    private UserService userService;

    @ApiOperation(value = "创建用户", notes = "根据User对象保存用户")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataType = "Long",
            paramType = "query"),
        @ApiImplicitParam(value = "用户名", name = "username", required = true, defaultValue = "admin",
            dataType = "String", paramType = "query"),
        @ApiImplicitParam(value = "密码", name = "password", required = true, defaultValue = "admin", dataType = "String",
            paramType = "query")})
    @ApiResponses(value = {@ApiResponse(code = 200, message = "保存用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @PostMapping("/save")
    public CommonResult<User> saveUser(@ApiIgnore User user) {
        return userService.saveUser(user);
    }

    @ApiOperation(value = "查询所有用户", notes = "查询所有用户")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询所有用户成功")})
    @GetMapping("/findAll")
    public CommonResult<List<User>> findUserAll() {
        return userService.findUserAll();
    }

    @ApiOperation(value = "根据id查询用户", notes = "查询指定id用户")
    @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataType = "Long")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id查询用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @GetMapping("/findById/{id}")
    public CommonResult<User> findUserById(@PathVariable(value = "id") Long id) {
        return userService.findUserById(id);
    }

    @ApiOperation(value = "根据id更新用户", notes = "更新指定id用户")
    @ApiImplicitParam(value = "用户", name = "user", required = true, dataType = "User", paramType = "body")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id更新用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @PutMapping("/updateById")
    public CommonResult<User> updateUserById(@RequestBody User user) {
        return userService.updateUserById(user);
    }

    @ApiOperation(value = "根据id删除用户", notes = "删除指定id的用户")
    @ApiImplicitParam(value = "用户id", name = "id", required = true, defaultValue = "1", dataType = "Long")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id删除用户成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteById/{id}")
    public CommonResult<User> deleteUserById(@PathVariable(value = "id") Long id) {
        return userService.deleteUserById(id);
    }

    @ApiOperation(value = "删除所有用户", notes = "删除所有用户")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "删除所有用户成功"),
        @ApiResponse(code = 404, message = "用户不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteAll")
    public CommonResult<List<User>> deleteUserAll() {
        return userService.deleteUserAll();
    }
}

分组order中的Controller:

package com.rtxtitanv.controller.order;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;
import com.rtxtitanv.service.OrderService;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.order.OrderController
 * @description OrderController
 * @date 2021/6/8 11:31
 */
@Api(value = "订单模块", tags = "订单管理", description = "订单的增删改查", protocols = "http")
@RequestMapping("/order")
@RestController
public class OrderController {

    @Resource
    private OrderService orderService;

    @ApiOperation(value = "创建订单", notes = "根据Order对象保存订单")
    @ApiImplicitParams(value = {
        @ApiImplicitParam(value = "订单id", name = "id", required = true, defaultValue = "1", dataType = "Long",
            paramType = "query"),
        @ApiImplicitParam(value = "订单编号", name = "orderNumber", required = true, defaultValue = "780829537365759918",
            dataType = "String", paramType = "query"),
        @ApiImplicitParam(value = "订单描述", name = "orderDescription", required = true, defaultValue = "二两牛肉面,微辣,多放点香菜",
            dataType = "String", paramType = "query"),
        @ApiImplicitParam(value = "订单所属用户id", name = "userId", required = true, defaultValue = "1", dataType = "Long",
            paramType = "query")})
    @ApiResponses(value = {@ApiResponse(code = 200, message = "保存订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class)})
    @PostMapping("/save")
    public CommonResult<Order> saveOrder(@ApiIgnore Order order) {
        return orderService.saveOrder(order);
    }

    @ApiOperation(value = "查询所有订单", notes = "查询所有订单")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "查询所有订单成功")})
    @GetMapping("/finAll")
    public CommonResult<List<Order>> findOrderAll() {
        return orderService.findOrderAll();
    }

    @ApiOperation(value = "根据id更新订单", notes = "更新指定id订单")
    @ApiImplicitParam(value = "订单", name = "order", required = true, dataType = "Order", paramType = "body")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id更新订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "订单不存在", response = CommonResult.class)})
    @PutMapping("/updateById")
    public CommonResult<Order> updateOrderById(@RequestBody Order order) {
        return orderService.updateOrderById(order);
    }

    @ApiOperation(value = "根据id删除订单", notes = "删除指定id订单")
    @ApiImplicitParam(value = "订单id", name = "id", required = true, defaultValue = "1", dataType = "Long")
    @ApiResponses(value = {@ApiResponse(code = 200, message = "根据id删除订单成功"),
        @ApiResponse(code = 400, message = "无效参数", response = CommonResult.class),
        @ApiResponse(code = 404, message = "订单不存在", response = CommonResult.class)})
    @DeleteMapping("/deleteById/{id}")
    public CommonResult<Order> deleteOrderById(@PathVariable(value = "id") Long id) {
        return orderService.deleteOrderById(id);
    }
}

五、Swagger2常用注解

这里总结一下前面用到的Swagger2的常用注解。

@Api:将一个类标记为Swagger资源。

  • value:隐式设置操作的标签,遗留支持。如果没有使用tags属性,该值将被用来为这个资源所描述的操作设置标签。否则,该值将被忽略。
  • tags:一个用于API文档控制的标签列表。标签可用于按资源或任何其他限定词对操作进行逻辑分组。一个非空的值将覆盖value属性中提供的值。
  • description:对API更详细的描述。已过期,保留为遗留支持。
  • protocols:为该资源下的操作设置特定的协议。
  • authorizations:对应Operation对象的security属性。 获取此资源下操作的授权列表。这可能会被特定的操作覆盖。
  • hidden:是否隐藏该资源的操作。默认值为false,不隐藏,如果API应该隐藏在swagger文档中,则为true。

@ApiOperation:描述一个操作或针对特定路径的HTTP方法。

  • value:对应operation的summary属性。对该操作进行简单的描述。
  • notes:对应operation的notes属性。对操作的详细描述。
  • tags:API文档控制的标签列表。

@ApiImplicitParams:用来描述方法上的多个参数。

@ApiImplicitParam:用来描述API操作中的单个参数。

  • value:参数的简要描述。
  • name:参数名称。
  • defaultValue:参数的默认值。
  • allowableValues:限制此参数的可接受值。
  • required:指定是否必需提供该参数。默认值为false,非必需,true,必需。
  • dataType:参数数据类型。可以是类名或原始数据类型。
  • dataTypeClass:参数的Class对象。提供会覆盖dataType
  • paramType:参数的参数类型,即参数放在哪个地方。有效值为path、query、body、header、form。
  • example:非body类型参数的一个示例。
  • examples:仅适用于body参数的一个示例。

@ApiResponses:用于表示一组响应。

@ApiResponse:用来描述可能的响应。

  • code:响应的HTTP状态码。
  • message:响应的消息。
  • response:用于描述响应消息的响应类。对应响应消息对象的schema属性。
  • responseHeaders:响应头列表。
  • responseContainer:声明包装响应的容器。有效值为List、Set、Map。任何其他值将被忽略。
  • examples:响应示例。

@ApiIgnore:用于忽略API。可以注解在类,方法,方法参数上,注解在类上忽略整个类,注解在方法上忽略该方法,注解在方法参数上会忽略该参数。

@ApiModel:提供有关swagger模型(Model)的额外信息。

  • value:为Model提供一个备用名称。默认情况使用类名。
  • description:提供更详细的类描述信息。
  • parent:为Model提供一个父类以允许描述继承。
  • discriminator:支持Model继承和多态,使用鉴别器的字段的名称,可以断言需要使用哪个子类型。
  • subTypes:继承自此模型的子类型的数组。
  • reference:指定对相应类型定义的引用,覆盖指定的任何其他元数据。

@ApiModelProperty:用于描述Model的属性信息。

  • value:属性的简要描述。
  • name:允许覆盖属性的名称。
  • allowableValues:限制此参数的可接受值。
  • access:允许从API文档中过滤属性。
  • notes:目前尚未使用。
  • dataType:参数的数据类型,可以是类名或原始数据类型,此值将覆盖从类属性读取的数据类型。
  • required:指定该参数是否必需,false,非必需,true,必需。
  • position:允许显式地对Model中的属性排序。
  • hidden:是否隐藏模型属性,false为不隐藏,true为隐藏。
  • example:属性示例值。
  • accessMode:允许指定Model属性(AccessMode. access)的访问模式。READ_ONLYREAD_WRITE,默认值AccessMode.AUTO
  • reference:指定对相应类型定义的引用,覆盖指定的任何其他元数据。
  • allowEmptyValue:是否允许传递空值,默认为false,不允许,true,允许。

六、Service层

package com.rtxtitanv.service;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;

import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.UserService
 * @description UserService
 * @date 2021/6/6 17:07
 */
public interface UserService {

    /**
     * 保存用户
     *
     * @param user 用户参数
     * @return CommonResult<User>
     */
    CommonResult<User> saveUser(User user);

    /**
     * 查询所有用户
     *
     * @return CommonResult<List<User>>
     */
    CommonResult<List<User>> findUserAll();

    /**
     * 根据id查询用户
     *
     * @param id 用户id
     * @return CommonResult<User>
     */
    CommonResult<User> findUserById(Long id);

    /**
     * 根据id更新用户
     *
     * @param user 用户参数
     * @return CommonResult<User>
     */
    CommonResult<User> updateUserById(User user);

    /**
     * 根据id删除用户
     *
     * @param id 用户id
     * @return CommonResult<User>
     */
    CommonResult<User> deleteUserById(Long id);

    /**
     * 删除所有用户
     *
     * @return CommonResult<List<User>>
     */
    CommonResult<List<User>> deleteUserAll();
}
package com.rtxtitanv.service.impl;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.User;
import com.rtxtitanv.service.UserService;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.impl.UserServiceImpl
 * @description UserService实现类
 * @date 2021/6/6 17:08
 */
@Service
public class UserServiceImpl implements UserService {

    /**
     * 创建线程安全的Map,模拟用户信息的存储
     */
    private static final Map<Long, User> USER_MAP = Collections.synchronizedMap(new HashMap<>());

    @Override
    public CommonResult<User> saveUser(User user) {
        if (user.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        USER_MAP.put(user.getId(), user);
        return CommonResult.success("保存用户成功", user);
    }

    @Override
    public CommonResult<List<User>> findUserAll() {
        return CommonResult.success("查询所有用户成功", new ArrayList<>(USER_MAP.values()));
    }

    @Override
    public CommonResult<User> findUserById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        return CommonResult.success("根据id查询用户成功", USER_MAP.get(id));
    }

    @Override
    public CommonResult<User> updateUserById(User user) {
        if (user.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (USER_MAP.get(user.getId()) == null) {
            throw new NotFoundException("用户不存在");
        }
        user = USER_MAP.get(user.getId()).setUsername(user.getUsername()).setPassword(user.getPassword());
        return CommonResult.success("根据id更新用户成功", user);
    }

    @Override
    public CommonResult<User> deleteUserById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (USER_MAP.get(id) == null) {
            throw new NotFoundException("用户不存在");
        }
        return CommonResult.success("根据id删除用户成功", USER_MAP.remove(id));
    }

    @Override
    public CommonResult<List<User>> deleteUserAll() {
        if (USER_MAP.isEmpty()) {
            throw new NotFoundException("用户不存在");
        }
        ArrayList<User> users = new ArrayList<>(USER_MAP.values());
        USER_MAP.clear();
        return CommonResult.success("删除所有用户成功", users);
    }
}
package com.rtxtitanv.service;

import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;

import java.util.List;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.OrderService
 * @description OrderService
 * @date 2021/6/8 15:45
 */
public interface OrderService {

    /**
     * 保存订单
     *
     * @param order 订单参数
     * @return CommonResult<Order>
     */
    CommonResult<Order> saveOrder(Order order);

    /**
     * 查询所有订单
     *
     * @return CommonResult<List<Order>
     */
    CommonResult<List<Order>> findOrderAll();

    /**
     * 根据id更新订单
     *
     * @param order 订单参数
     * @return CommonResult<Order>
     */
    CommonResult<Order> updateOrderById(Order order);

    /**
     * 根据id删除订单
     *
     * @param id 订单id
     * @return CommonResult<Order>
     */
    CommonResult<Order> deleteOrderById(Long id);
}
package com.rtxtitanv.service.impl;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import com.rtxtitanv.model.Order;
import com.rtxtitanv.service.OrderService;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.service.impl.OrderServiceImpl
 * @description OrderService实现类
 * @date 2021/6/8 15:45
 */
@Service
public class OrderServiceImpl implements OrderService {

    /**
     * 创建线程安全的Map,模拟订单信息的存储
     */
    private static final Map<Long, Order> ORDER_MAP = Collections.synchronizedMap(new HashMap<>());

    @Override
    public CommonResult<Order> saveOrder(Order order) {
        if (order.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        ORDER_MAP.put(order.getId(), order);
        return CommonResult.success("保存订单成功", order);
    }

    @Override
    public CommonResult<List<Order>> findOrderAll() {
        return CommonResult.success("查询所有订单成功", new ArrayList<>(ORDER_MAP.values()));
    }

    @Override
    public CommonResult<Order> updateOrderById(Order order) {
        if (order.getId() <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (ORDER_MAP.get(order.getId()) == null) {
            throw new NotFoundException("订单不存在");
        }
        order = ORDER_MAP.get(order.getId()).setOrderNumber(order.getOrderNumber())
            .setOrderDescription(order.getOrderDescription()).setUserId(order.getUserId());
        return CommonResult.success("根据id更新订单成功", order);
    }

    @Override
    public CommonResult<Order> deleteOrderById(Long id) {
        if (id <= 0) {
            throw new InvalidParameterException("无效参数");
        }
        if (ORDER_MAP.get(id) == null) {
            throw new NotFoundException("订单不存在");
        }
        return CommonResult.success("根据id删除订单成功", ORDER_MAP.remove(id));
    }
}

七、全局异常处理

自定义异常类:

package com.rtxtitanv.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.exception.InvalidParameterException
 * @description 自定义异常类,无效参数异常
 * @date 2021/6/8 17:23
 */
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class InvalidParameterException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = 4880076621322329751L;
    private String message;
}
package com.rtxtitanv.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.exception.NotFoundException
 * @description 自定义异常类,数据不存在的异常
 * @date 2021/6/8 17:23
 */
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class NotFoundException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = -7962596135697516276L;
    private String message;
}

全局异常处理类:

package com.rtxtitanv.handler;

import com.rtxtitanv.exception.InvalidParameterException;
import com.rtxtitanv.exception.NotFoundException;
import com.rtxtitanv.model.CommonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.handler.GlobalExceptionHandler
 * @description 全局异常处理类
 * @date 2021/6/8 17:34
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(InvalidParameterException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public CommonResult<Object> invalidParameterException(InvalidParameterException e) {
        String message = e.getMessage();
        LOGGER.error("异常信息:" + message, e);
        return CommonResult.invalidParameter(message);
    }

    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(code = HttpStatus.NOT_FOUND)
    public CommonResult<Object> notFoundException(NotFoundException e) {
        String message = e.getMessage();
        LOGGER.error("异常信息:" + message, e);
        return CommonResult.notFound(message);
    }
}

八、运行项目查看文档

启动SpringBoot项目,访问Swagger-UI的地址http://localhost:8080/swagger-ui.html查看接口文档:
1
切换到user分组:
2
切换到order分组:
3
创建用户接口如下:
4
创建用户接口的响应信息如下:
5
更新用户接口如下:
6
用户管理API的Model:
7

九、测试接口

可以在API文档中进行接口测试。首先测试创建用户接口以保存用户,点击下图中的Try it out按钮:
8
输入参数后点击Execute:
9
测试结果如下,可见保存成功:
10
在进行查询测试前再保存2个数据,然后测试查询所有用户:
11
测试根据id更新用户接口:
12
测试根据id删除用户接口:
13
下面测试抛出自定义异常的情况,测试根据id查询用户接口,输入一个无效id:
14
测试根据id更新用户接口,更新一个不存在的用户:
15

十、引入knife4j

Knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。前身是swagger-bootstrap-ui。

首先引入依赖:

<!--  knife4j 起步依赖 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.7</version>
</dependency>

注意:knife4j的版本需要根据引入的springfox版本而定。如果底层依赖的springfox框架版本是2.9.2,knife4j的版本使用2.02.0.5。如果底层springfox框架版本升级至`2.10.5`,OpenAPI规范是v2,knife4j的版本使用2.0.6。如果底层依赖springfox框架版本升级至3.0.3,OpenAPI规范是v3,knife4j的版本使用3.0~。

重启SpringBoot项目,访问knife4j的地址http://localhost:8080/doc.html查看接口文档:
16
切换到user分组:
17
切换到order分组:
18

代码示例

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,你想要了解如何在Spring Boot 2.7.9中整合Swagger2并使用com.github.xiaoymin的knife4j扩展库,是吗?我可以为你提供一些步骤。 首先,你需要在你的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>2.0.8</version> </dependency> ``` 然后,你需要在你的Spring Boot配置类中添加以下内容: ```java @Configuration @EnableSwagger2 public class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("Swagger2接口文档") .description("Swagger2接口文档") .termsOfServiceUrl("http://localhost:8080/") .version("1.0") .build(); } } ``` 这个配置类将创建一个名为"Swagger2接口文档"的文档,并扫描com.example.demo.controller包中的所有控制器。 最后,你可以在你的浏览器中访问http://localhost:8080/doc.html来查看生成的文档。 如果你想自定义Swagger UI的主题,你可以在application.properties中添加以下配置: ```properties # Swagger UI主题 knife4j.swaggerui.path=/doc.html knife4j.swaggerui.title=Swagger2接口文档 knife4j.swaggerui.description=Swagger2接口文档 knife4j.swaggerui.version=1.0 knife4j.swaggerui.contact.name=联系人姓名 knife4j.swaggerui.contact.email=联系人邮箱 knife4j.swaggerui.contact.url=联系人网址 knife4j.swaggerui.license.name=许可证名称 knife4j.swaggerui.license.url=许可证网址 knife4j.swaggerui.enable=true # 配置主题 knife4j.swaggerui.theme=flattop ``` 这将启用knife4j并使用flattop主题。 希望这些步骤对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RtxTitanV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值