SpringBoot使用swagger并通过Knife4j美化swagger页面,利用swagger注解完成详细日志记录信息

752 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

一、swagger

相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。

下面来使用swagger3来进行演示说明,针对swagger2,官网文档总结主要修改如下几点:

  • 1、删除了对springfox-swagger2的依赖;
  • 2、删除所有@EnableSwagger2…注解;
  • 3、添加了springfox-boot-starter依赖项;
  • 4、移除了guava等第三方依赖;
  • 5、文档访问地址改为http://ip:port/project/swagger-ui/index.html。

1-1、swagger的添加

1-1-1、添加依赖

目前swagger已经出3.0版本,因此我这边直接使用3.0版本,springboot使用2.6.7。因此直接引入swagger的启动器即可。

老版本可以引入如下依赖:

<!--
    swagger 是一系列REST接口的描述和UI展示的规范(json)
    springfox 整合springmvc和swagger 将代码中的注解转换为符合swagger的规范的json文件
-->
<!-- 核心包 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- ui界面的依赖 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

我这边引入依赖为:

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

1-1-2、创建swagger配置类

需要注意的是引入的Contact一定要使用swaggerfox的。这个配置类主要告诉swagger怎么去生成规范的数据,其中包括一些文档相关信息,自己可根据需求进行修改

@Configuration
@EnableOpenApi
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2) //生成swagger2规范的文档
                .pathMapping("/") //设置哪些接口映射到文档中
                .select() //接口选择器,
                .apis(RequestHandlerSelectors.basePackage("com.jony.controller")) //告诉springfox哪些接口需要生成swagger文档
                .paths(PathSelectors.any()) //设置哪些接口会生成在swagger文档上(下面设置的是所有)
                //下面为描述文档的主体信息--可根据自己需要修改
                .build().apiInfo(new ApiInfoBuilder()
                        .title("SpringBoot整合Swagger")
                        .description("SpringBoot整合Swagger,详细信息......")
                        .version("1.0")
                        .contact(new Contact("jony","www.jony.cn","123@qq.com"))
                        .build());
    }
}

1-1-3、启动调试

这时候启动会报错,导致原因为:Springfox的Spring MVC 的路径匹配策略是 ant-path-matcher,而 Spring Boot 2.6.x版本的默认匹配策略是 path-pattern-matcher,这就造成了下面的报错。

image.png

1-1-3-1、springboot2.6.x添加swagger3.0错误解决

上面提到由于springfox和springboot的默认策略不一致导致,因此我们在配置文件里面修改springboot的策略为ant-path-matcher即可,如下:

image.png 这样就可以启动成功了,下面我们进行访问swagger3.0默认访问地址为:http://localhost:8080/swagger-ui/index.html

image.png 可以看到上部分即为我们在配置文件中设置的swagger相关信息,下面即为我们的控制器里面的方法以及请求类型。

通过以上可以解决报错并且可以对swagger进行访问,但是在某些服务启动时会失效,原因是使用springboot执行器的时候就失效了。因为执行器将始终使用基本路径模式的解析也就是默认策略(path-pattern-matcher)。因此在springboot2.6.x及高版本中并且与执行器一起使用时,则需要对Springfox进行修改。解决方案我这边也是在网上找到的方案,具体就是在swagger配置类中添加如下bean:

@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");
                field.setAccessible(true);
                return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    };
}

最终是否起作用,后面在使用执行器的时候再进行测试,如果大家有兴趣,可以去测试一下。

1-2、使用swagger测试

通过刚刚打开的swagger不仅可以看到我们控制器中的方法,同时还可以进行调用测试,下面来进行操作一下

1-2-1、点击要测试的接口

image.png

1-2-2、点击try it out

点击try it out,并输入参数,点击Execute

image.png

1-2-3、查看返回数据信息

image.png

通过以上方式我们就可以使用swagger进行接口调用测试了,同时也可以给前端人员,让前端人员进行接口调用。不过刚刚看到的全是英文界面,包括Controller以及里面的接口方法和接口需要传的参数都没有说明,这样是极其不友好的,别说前端人员,即使我们自己再过段时间,也不知道接口是干嘛的了,因此我们需要对相关信息添加一些描述信息。

1-3、swagger的相关注解

1-3-1、注解整体说明

1-3-1-1、用于 controller 类上

注解说明
@Api对请求类的说明
1-3-1-1-1、在Controller中使用

image.png

1-3-1-1-2、打开swagger页面查看

image.png

1-3-1-1-3、@Api其他参数描述说明

image.png

1-3-1-2、用于方法上面 (说明参数的含义):

注解说明
@ApiOperation方法的说明
@ApiImplicitParams、@ApiImplicitParam方法的参数的说明;@ApiImplicitParams 用于指定单个参数的说明
1-3-1-2-1、在代码中使用

一个参数使用方法

@ApiOperation("通过用户ID查询用户信息")
@ApiImplicitParam(name="id",value = "用户ID")
@GetMapping("/{id}")
public Result getUser(@PathVariable Integer id) {
    User user = userService.getUserById(id);
    return new Result<>(200, "messge", user);
}

多参数使用方法

@ApiOperation(value="用户登录",notes="随边说点啥")
@ApiImplicitParams({
        @ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),
        @ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),
        @ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")
})
@PostMapping("/login")
public Result login(@RequestParam String mobile, @RequestParam String password,
                        @RequestParam Integer age){
    //...
    return new Result<>(200, "messge");
}
1-3-1-2-2、swagger界面查看方法和参数描述效果

image.png

1-3-1-2-3、@ApiOperation的相关其他参数说明

image.png image.png

1-3-1-2-4、@ApiImplicitParams的相关其他参数说明

image.png image.png

1-3-1-3、用于方法上面 (返回参数或对象的说明):

返回这个一般不使用

注解说明
@ApiResponses、@ApiResponse方法返回值的说明 ;@ApiResponses 用于指定单个参数的说明

1-3-1-4、对象类:

注解说明
@ApiModel用在 JavaBean 类上,说明 JavaBean 的 用途
@ApiModelProperty用在 JavaBean 类的属性上面,说明此属性的的含议
1-3-1-4-1、在模型中使用@ApiModel和@ApiModelProperty

在控制器中,我们会经常使用我们的对象模型,使用@ApiModel就可以给对象模型进行说明,并且使用@ApiModelProperty给模型属性进行说明

image.png

1-3-1-4-2、在swagger中查看效果

如下在Model中已经对user做出了相关描述 image.png

1-3-1-4-3、@ApiModel的其他相关参数说明

image.png

1-3-1-4-4、@ApiModelProperty的其他参数说明

image.png

以上即为swagger的相关经常使用的注解,其他相关注解感兴趣的同学可以自行搜索使用。

1-4、swagger页面的美化及文档下载

通过上的配置使用,我们就可以比较顺畅的使用swagger了,但是页面实在有点接受不了,同时文档也没办法导出,下面我们就开始进行页面的美化及文档导出。

1-4-1、添加依赖

<!--整合Knife4j-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

1-4-2、在swagger的配置类上添加@EnableKnife4j

image.png

1-4-3、测试访问

之前我这边本地访问的时候使用:
http://localhost:8080/swagger-ui/index.html
如果还是用这个连接是不行的。

我们需要使用如下地址:
http://localhost:8080/doc.html

效果展示:

image.png 是不是漂亮了很多,兼职天壤之别啊。

1-4-4、使用knife4j的swagger

1-4-4-1、控制器说明

下面这块就会把我们所有的控制器展示出来,点击之后,也可以看到里面的接口,以及对应的请求类型

image.png

1-4-4-1-1、查看接口

点击接口,就可以查看接口的详细信息,请求信息、响应信息一目了然 image.png

1-4-4-1-2、接口调试

输入相关参数,点击发送,就可以看到相关返回的数据信息 image.png

1-4-4-2、文档管理

在文档管理中、就可以添加全局参数、下载文档、以及相关个性化设置

1-4-4-2-1、全局参数设置

image.png

1-4-4-2-2、离线文档下载

image.png

1-4-4-2-3、个性化设置

image.png

1-4-4-3、Models查看

在swagger Models中就可以查看相关的model信息了。 image.png

二、通过AOP用swagger注解实现日志功能

一般日志,我们仅会输出调用当前方法的一些简略信息,这样在后面进行日志解读的时候多少有些不方便,比如调用了方法的名称、参数名称等。下面我们就可以使用AOP来实现日志功能

2-1、引入aop启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2-2、编写aop类

@Aspect
@Component
public class LogAspect {

    @Around("execution(* com.jony.controller..*.*(..)) && @annotation(apiOperation)")
    public Object around(JoinPoint pjp,ApiOperation apiOperation) {
        Object result = null;
        try {
            //获取类对象
            Class<?> controller = pjp.getThis().getClass();
            //获取swagger接口对象
            Api annotation = controller.getAnnotation(Api.class);
            //获取类接口的内容

            System.out.println("调用接口为:"+ annotation.value()+"-"+apiOperation.value());

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return result;
    }

}

2-3、调用接口查看输出信息

我这边只是简单的输出了一下,相关AOP的知识点,可以查看我之前的文章

image.png