面试题一览

第一章-Swagger概述

1什么是Swagger

前后端分离

  • 前端 -> 前端控制层、视图层
  • 后端 -> 后端控制层、服务层、数据访问层
  • 前后端通过API进行交互
  • 前后端相对独立且松耦合

产生的问题

  • 前后端集成,前端或者后端无法做到“及时协商,尽早解决”,最终导致问题集中爆发

  • 早起使用word计划文档

  • 前后分离:

    • 前段测试后端接口:postman
    • 后端提供结构,需要实时更新最新消息以及改动。

解决方案

  • 首先定义schema [ 计划的提纲 ],并实时跟踪最新的API,降低集成风险

Swagger

  • 号称世界上最流行的API框架
  • Restful Api 文档在线自动生成器 => API 文档 与API 定义同步更新
  • 直接运行,在线测试API
  • 支持多种语言 (如:Java,PHP等)
  • 官网:https://swagger.io/

2SpringBoot集成Swagger

SpringBoot集成Swagger => springfox,两个jar包

  • Springfox-swagger2
  • swagger-springmvc

使用Swagger

要求:jdk 1.8 + 否则swagger2无法运行

2、添加Maven依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger2</artifactId>
   <version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

3、编写HelloController,测试确保运行成功!

4、要使用Swagger,我们需要编写一个配置类-SwaggerConfig来配置 Swagger

1
2
3
4
@Configuration //配置类
@EnableSwagger2// 开启Swagger2的自动配置
public class SwaggerConfig {  
}

5、访问测试 :http://localhost:8080/swagger-ui.html ,可以看到swagger的界面;

3Swagger配置

1、Swagger实例Bean是Docket,所以通过配置Docket实例来配置Swaggger。

1
2
3
4
5
6
7
8
9
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean //配置docket以配置Swagger具体参数
    public Docket docket() {
    //    return new Docket(DocumentationType.SWAGGER_2);
          return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
    }
}

2、可以通过apiInfo()属性配置文档信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//配置文档信息
private ApiInfo apiInfo() {
   Contact contact = new Contact("联系人名字", "http://xxx.xxx.com/联系人访问链接","联系人邮箱");
   return new ApiInfo(
           "Swagger学习", // 标题
           "学习演示如何配置Swagger", // 描述
           "v1.0", // 版本
           "http://terms.service.url/组织链接", // 组织链接
           contact, // 联系人信息
           "Apach 2.0 许可", // 许可
           "许可链接", // 许可连接
           new ArrayList<>()// 扩展
  );
}

3、Docket 实例关联上 apiInfo()

1
2
3
4
@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}

4、重启项目,访问测试 http://localhost:8080/swagger-ui.html 看下效果;

4配置扫描接口

1、构建Docket时通过select()方法配置怎么扫描接口。(主要使用这个方式就好)

1
2
3
4
5
6
7
8
@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
      .build();
}

2、重启项目测试,由于我们配置根据包的路径扫描接口,所以我们只能看到一个类

3、除了通过包路径配置扫描接口外,还可以通过配置其他方式扫描接口,这里注释一下所有的配置方式:

1
2
3
4
5
6
7
8
any() // 扫描所有,项目中的所有接口都会被扫描到
none() // 不扫描接口
// 通过方法上的注解扫描,如withMethodAnnotation(GetMapping.class)只扫描get请求
withMethodAnnotation(final Class<? extends Annotation> annotation)
  
// 通过类上的注解扫描,如.withClassAnnotation(Controller.class)只扫描有controller注解的类中的接口
withClassAnnotation(final Class<? extends Annotation> annotation)
basePackage(final String basePackage) // 根据包路径扫描接口

4、除此之外,我们还可以配置接口扫描过滤:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       // 配置如何通过path过滤,即这里只扫描请求以/kuang开头的接口
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

5、paths的可选值还有

1
2
3
4
any() // 任何请求都扫描
none() // 任何请求都不扫描
regex(final String pathRegex) // 通过正则表达式控制
ant(final String antPattern) // 通过ant()控制
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//多个troller 没有实际运用
{
 com.google.common.base.Predicate<RequestHandler> selector1 = RequestHandlerSelectors.basePackage("com.share.modules.user.controller");
 com.google.common.base.Predicate<RequestHandler> selector2 = RequestHandlerSelectors.basePackage("com.share.modules.resource.controller");
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            .select()
            .apis(Predicates.or(selector1,selector2))
            .paths(PathSelectors.any())
            .build()
            .globalOperationParameters(setHeaderToken());
}

5配置Swagger开关

1、通过enable()方法配置是否启用swagger,如果是false,swagger将不能在浏览器中访问了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Bean
public Docket docket() {
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .enable(false) //配置是否启用Swagger,如果是false,在浏览器将无法访问
      .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       // 配置如何通过path过滤,即这里只扫描请求以/kuang开头的接口
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

2、如何动态配置当项目处于test、dev环境时显示swagger,处于prod时不显示? 需要配置多个环境,见springboot配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Bean
public Docket docket(Environment environment) {
   // 设置要显示swagger的环境
   Profiles of = Profiles.of("dev", "test");
   // 判断当前是否处于该环境
   // 通过 enable() 接收此参数判断是否要显示
   boolean b = environment.acceptsProfiles(of);
   
   return new Docket(DocumentationType.SWAGGER_2)
      .apiInfo(apiInfo())
      .enable(b) //配置是否启用Swagger,如果是false,在浏览器将无法访问
      .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
      .apis(RequestHandlerSelectors.basePackage("com.kuang.swagger.controller"))
       // 配置如何通过path过滤,即这里只扫描请求以/kuang开头的接口
      .paths(PathSelectors.ant("/kuang/**"))
      .build();
}

3、可以在项目中增加一个dev的配置文件查看效果!

6配置API分组

==可以设置不同的controller为不同的分组==

img

1、如果没有配置分组,默认是default。通过groupName()方法即可配置分组:

1
2
3
4
5
6
@Bean
public Docket docket(Environment environment) {
   return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
      .groupName("hello") // 配置分组
       // 省略配置....
}

2、重启项目查看分组

3、如何配置多个分组?配置多个分组只需要配置多个docket即可:

这样就可以查看各个不同的controller了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Bean
public Docket docket1(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group1");
}
@Bean
public Docket docket2(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group2");
}
@Bean
public Docket docket3(){
   return new Docket(DocumentationType.SWAGGER_2).groupName("group3");
}

4、重启项目查看即可

7实体配置

注意:实体类的配置只会在使用到的时候会出现,比如在controller中返回的类型值为这个实体。

1、新建一个实体类

写实体类value最好 加上类名

1
2
3
4
5
6
7
@ApiModel(value="用户实体",description="")
public class User {
   @ApiModelProperty("用户名")
   public String username;
   @ApiModelProperty("密码")
   public String password;
}

2、只要这个实体在请求接口的返回值上(即使是泛型),都能映射到实体项中:

1
2
3
4
@RequestMapping("/getUser")
public User getUser(){
   return new User();
}

3、重启查看测试

img

注:并不是因为@ApiModel这个注解让实体显示在这里了,而是只要出现在接口方法的返回值上的实体都会显示在这里,而@ApiModel和@ApiModelProperty这两个注解只是为实体添加注释的。

@ApiModel为类添加注释

@ApiModelProperty为类属性添加注释

8常用注解

Swagger的所有注解定义在io.swagger.annotations包下

作用范围 API 使用位置 对象属性 @ApiModelProperty 用在出入参数对象的字段上 协议集描述 @Api 用于controller类上 协议描述 @ApiOperation 用在controller的方法上 Response集 @ApiResponses 用在controller的方法上 Response @ApiResponse 用在 @ApiResponses里边 非对象参数集 @ApiImplicitParams 用在controller的方法上 非对象参数描述 @ApiImplicitParam 用在@ApiImplicitParams的方法里边 描述返回对象的意义 @ApiModel 用在返回对象类上

作用范围 API 使用位置
对象属性 @ApiModelProperty 用在出入参数对象的字段上
协议集描述 @Api 用于controller类上
协议描述 @ApiOperation 用在controller的方法上
Response集 @ApiResponses 用在controller的方法上
Response @ApiResponse 用在 @ApiResponses里边
非对象参数集 @ApiImplicitParams 用在controller的方法上
非对象参数描述 @ApiImplicitParam 用在@ApiImplicitParams的方法里边
描述返回对象的意义 @ApiModel 用在返回对象类上

1Controller上的

Swagger注解 简单说明
1@Api(tags = “api用途说明,用于分组”,value="接口描述”) 作用在模块类上(请求类)
2@ApiOperation(value="xxx接口方法说明”,notes="接口详细描述”,tags="选择已有分组,或新建”) 作用在接口方法上(controller方法)
3@ApiImplicitParams 用于说明多个请求参数
4@ApiIgnore: 作用于接口入参参数列表 表示swagger忽略该入参
5@ApiParam(“xxx参数说明”)类似@ApiImplicitParams 参数说明,和上面3用法类似
6@ApiResponse返回结果

在写mabatis代码时,有时实体类有级联实体的,这个实体又用在请求参数上,会造成文档参数的多余。所以,可以重写一个类,作为参数。


** 详解或例子**

1@ApiOperation

1
2
3
4
5
6
7
@ApiOperation
value:接口用途(必选) 直接在注解后括号内写,即value的值,会在swagger-ui里显示,具体示例如下
  notes:接口备注说明(可选)
  httpMethod:请求方式(可选)
  response:返回参数类型(可选)
  tags:接口分组名(可选)
  hidden:隐藏

2@Api的tags标签。用来分组,前面的大黑体字。 tags可以有多个参数{“分组1”,“分组2”}

image-20200714091552315

3@ApiImplicitParams

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@ApiImplicitParams:可选当描述多个@ApiImlicitParam时使用,即描述多个参数的时候使用
   @ApiImplicitParam(
  接口入参描述本身可选对应的子字段也可选
  name参数属性名
  value参数说明
  required是否必传 true/false
  paramType请求参数的获取方式
    header@RequestHeader 从请求头获取
    query@RequestParam 从请求参数获取
    path@PathVariable 从请求的路径获取这也是使用动态(REST风格)请求url的使用的注解
    body:@RequestBody 使用此注解可以选择接收参数为对象类型(JSON)
    form极少使用请求表单中获取
  dataType参数类型
  defaultValue参数默认值,用swagger时会默认填充
)

@ApiImplicitParams( //用在请求参数,对请求参数进行说明
        @ApiImplicitParam(name = "id",
                value = "admin的主键Id value"
                ,defaultValue="201801"
                //required 可选参数
        )
)

img

6@ApiResponses

1
2
3
4
5
@ApiResponses:(接口返回结果;可选,当描述多个@ApiResponse时候使用)
  @ApiResponse:(可选)
    codeHTTP请求返回码。(必选)
    message:返回信息。(必选)
    response:返回类型,需使用全类名。eg"com.xxx.dto.DemoRequestDTO.class"(可选)

2实体类上面的

Swagger注解 简单说明
1@ApiModel(value="xxxPOJO说明”,descrition=”") 作用在模型类上:如VO、BO
2@ApiModelProperty(value = “xxx属性说明”,hidden = true,required=true 参数是否必选,example–举例说明) 作用在类方法和属性上,hidden设置为true可以隐藏该属性

1@ApiModel

image-20200714102116588

2@ApiModelProperty

image-20200714102749542

正式环境要记得关闭Swagger,一来出于安全考虑二来节省运行时内存。

3请求参数忽略

当一个实体类,级联操作时,那么就会有参数。

可以对参数使用@ApiIgnore注解,再用@ApiImplicitParam

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@ApiImplicitParams({
     @ApiImplicitParam(name = "page", value = "分页参数:当前页", defaultValue = "1", required = false),
     @ApiImplicitParam(name = "limit", value = "分页参数:每页数量", defaultValue = "10", required = false),
     @ApiImplicitParam(name = "loginname", value = "登录名称", required = false),
     @ApiImplicitParam(name = "loginip", value = "IP", required = false),
     @ApiImplicitParam(name = "startTime", value = "登录起始时间", required = false),
     @ApiImplicitParam(name = "endTime", value = "登录终止时间", required = false)
})
public Result page(@ApiIgnore LogVo vo) {
    return null;
}

9总结/风格

推荐前两个UI (可以有其它的ui,可以上网查询)

http://localhost:8080/swagger-ui.html 可以直接测试方法。

我们可以导入不同的包实现不同的皮肤定义:

1、默认的 访问 http://localhost:8080/swagger-ui.html

1
2
3
4
5
<dependency> 
   <groupId>io.springfox</groupId>
   <artifactId>springfox-swagger-ui</artifactId>
   <version>2.9.2</version>
</dependency>

2、bootstrap-ui 可以把接口文档进行导出 访问 http://localhost:8080/doc.html

1
2
3
4
5
6
<!-- 引入swagger-bootstrap-ui包 /doc.html-->
<dependency>
   <groupId>com.github.xiaoymin</groupId>
   <artifactId>swagger-bootstrap-ui</artifactId>
   <version>1.9.1</version>
</dependency>

4、mg-ui 访问 http://localhost:8080/document.html

1
2
3
4
5
6
<!-- 引入swagger-ui-layer包 /document.html-->
<dependency>
   <groupId>com.zyplayer</groupId>
   <artifactId>swagger-mg-ui</artifactId>
   <version>1.0.6</version>
</dependency>

第二章-结合的技术

4Result风格

https://blog.kuangstudy.com/index.php/archives/466/

这里简单介绍一下,以后用到时再行补充。

Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

功能

  • 资源:互联网所有的事物都可以被抽象为资源
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
  • 分别对应 添加、 删除、修改、查询。

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

  • http://127.0.0.1/item/queryItem.action?id=1 查询,GET
  • http://127.0.0.1/item/saveItem.action 新增,POST
  • http://127.0.0.1/item/updateItem.action 更新,POST
  • http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST

使用RESTful操作资源 : 可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

  • http://127.0.0.1/item/1 查询,GET
  • http://127.0.0.1/item 新增,POST
  • http://127.0.0.1/item 更新,PUT
  • http://127.0.0.1/item/1 删除,DELETE

请求方法的注解, @GetMapping 是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。 @GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping

在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上。

比如网页访问http://localhost:8080/commit/1/2,结果为3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Controller
public class RestFulController {

    //映射访问路径
    @RequestMapping("/commit/{p1}/{p2}")
    public String index(@PathVariable int p1, @PathVariable int p2, Model model){     
        int result = p1+p2;
        //Spring MVC会自动实例化一个Model对象用于向视图中传值
        model.addAttribute("msg", "结果:"+result);
        //返回视图位置
        return "test";      
    }   
}