从.Net到Java学习第五篇——Spring Boot &&Profile &&Swagger2

从.Net 到 Java 学习系列目录

刚学 java 不久,我有个疑问,为何用到的各种 java 开源 jar 包许多都是阿里巴巴的开源项目,为何几乎很少见百度和腾讯?不是说好的 BAT 吗?

Spring Boot 的配置文件及多环境配置

  Spring Boot 使用一个全局的配置文件 application.properties 或 application.yml,放置在 src/main/resources 目录或者类路径的 /config 下。
  Spring Boot 的全局配置文件的作用是对一些默认配置的配置值进行修改。
  例如将 Tomcat 的默认端口号 8080 修改为 8082,并将默认的访问路径 “/” 修改为 “/boot”。可以在 application.yml 中添加:

server:
  port: 8082
  context-path: /boot

  如果我们需要在不同的环境下面有不同的配置怎么办?比如生产、开发、测试三个不同的环境,我们的配置肯定不一样。这时,我们需要用到 Profile。

  Profile 是 Spring 用来针对不同的环境对不同的配置提供支持的,全局 Profile 配置使用 application-{profile}. yml(如 application-prod.yml)。通过在 application.yml 中设置 spring.profiles.active = prod 来指定活动的 Profile。

依次再目录下面新建三个配置文件,application-dev.yml、application-test.yml、application-prod.yml。它们分别代表开发环境、测试环境、生产环境的配置文件。

application-dev.yml:

server:
  port: 8083
  context-path: /boot

application-test.yml:

server:
  port: 8085
  context-path: /boot

application-prod.yml:

server:
  port: 8084
  context-path: /boot

接下来修改 application.yml:表示,将采用 application-dev.yml 这个配置文件。

spring:
  profiles:
    active: dev

我们在 IDEA 中运行项目,然后看下运行结果:

我们看到启用了 8083 端口,而我们的配置文件 application-dev.yml 中正是配置的 8083 端口。

假设我们在 application.yml 中已经配置了端口 8082,看下会怎么样

运行结果:

Tomcat started on port(s): 8083 (http)

还是使用的 dev 中 8083 端口,那么我们再来换下位置,把

server:
  port: 8082
  context-path: /boot

放到配置文件的最后面,再看下结果,结果还是启用的 8083 端口。

那么说明,配置文件会优先获取 Profile 中的配置,如果 Profile 中没有的配置项, 那么会直接取 application.yml 中的配置。

 Spring Boot 实现 RestFul

回到之前AreaController类的代码:

    @Autowired
    private AreaService areaService;
    @RequestMapping(value = "/get", produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> getArea(@PathParam("areaId")Integer areaId){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("area",areaService.getArea(areaId));
        return modelMap;
    }

我们的通过 URL 访问的方式是这样的:通过?号传递参数,而且要求问号后面的参数名称必须和 @PathParam("areaId")中的参数名称保持一致,显然这样是不符合 RestFul 风格的。

@PathParam 注解接收的是传统的 URL 界面传参的方式

接下来,我们稍微修改一下:

    @RequestMapping(value = "/get/{areaId}", produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> getAreaApi(@PathVariable Integer areaId){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("area",areaService.getArea(areaId));
        return modelMap;
    }

再看下允许结果:

我们再来修改一下代码看下:

    @RequestMapping(value = "/get/{Id}", produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> getAreaApi(@PathVariable("Id")Integer areaId){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("area",areaService.getArea(areaId));
        return modelMap;
    }

运行结果还是一样的。那么这说明,当 @PathVariable 中不指定参数名称的时候,默认就是把后面的 areaId 当成是接收参数了,如果 @PathVariable 中指定了接收参数的名称(这个名称必须和 {Id}一致),那么后面的Integer areaId可以随意命名。

@PathVariable 注解接收的就是符合 RestFull 风格的参数。此外还有一个 @RequestParam 注解。

@RequestParam 和 @PathVariable 注解是用于从 request 中接收请求的,两个都可以接收参数,关键点不同的是 @RequestParam 是从 request 里面拿取值,而 @PathVariable 是从一个 URI 模板里面来填充。

SpringBoot 整合 Swagger2 自动生成 API 文档

手写 Api 文档的几个痛点:
    文档需要更新的时候,需要再次发送一份给前端,也就是文档更新交流不及时。
    接口返回结果不明确
    不能直接在线测试接口,通常需要使用工具,比如 postman
    接口文档太多,不好管理
Swagger 也就是为了解决这个问题,当然也不能说 Swagger 就一定是完美的,当然也有缺点,最明显的就是代码移入性比较强。asp.net web api 也有 api 文档自动生成功能,最终效果和这个差不多。

(1)添加 Swagger 依赖

修改我们的 pom.xml 文件,添加变量 <springfox.version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <springfox.version>2.7.0</springfox.version>
    </properties>

引入依赖

  <!--swagger2-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.version}</version>
        </dependency>

(2)添加Swagger2的配置文件Swagger2Config

在 config 包下面新建类Swagger2Config

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.yujie.controller")) //需要注意的是这里要写入控制器所在的包
                .paths(PathSelectors.any())
                .build();}
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> ApiInfo apiInfo() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ApiInfoBuilder()
            .title(</span>"springboot利用swagger构建api文档"<span style="color: rgba(0, 0, 0, 1)">)
            .description(</span>"简单优雅的restfun风格,https://www.cnblogs.com/jiekzou/"<span style="color: rgba(0, 0, 0, 1)">)
            .termsOfServiceUrl(</span>"https://www.cnblogs.com/jiekzou/"<span style="color: rgba(0, 0, 0, 1)">)
            .version(</span>"1.0"<span style="color: rgba(0, 0, 0, 1)">)
            .build();
}

}

注意:用 @Configuration 注解该类,等价于 XML 中配置 beans;用 @Bean 标注方法等价于 XML 中配置 bean。

如上代码所示,通过 @Configuration 注解,让 Spring 来加载该类配置。再通过 @EnableSwagger2 注解来启用 Swagger2。

createRestApi 函数创建 Docket 的 Bean 之后,apiInfo()用来创建该 Api 的基本信息(这些基本信息会展现在文档页面中)。select() 函数返回一个 ApiSelectorBuilder 实例用来控制哪些接口暴露给 Swagger 来展现,本例采用指定扫描的包路径来定义,Swagger 会扫描该包下所有 Controller 定义的 API,并产生文档内容(除了被 @ApiIgnore 指定的请求)。

添加文档内容

在完成了上述配置后,其实已经可以生产文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。如下所示,我们通过 @ApiOperation 注解来给 API 增加说明、通过 @ApiImplicitParams、@ApiImplicitParam 注解来给参数增加说明。

查看我们AreaController类的代码如下:

@Api(value = "区域操作 controller", description = "区域相关的操作", tags = {"区域模块校验接口"})
@RestController
//@RequestMapping("/area")
public class AreaController {
    @Autowired
    private AreaService areaService;
/*    @RequestMapping(value = "/get", produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> getArea(@PathParam("areaId") Integer areaId){Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("area",areaService.getArea(areaId));
        return modelMap;
    }*/
    @ApiOperation(value="获取区域详细信息", notes="根据 url 的 id 来获取区域详细信息")
    @ApiImplicitParam(name = "Id", value = "区域 ID", required = true, dataType = "Integer", paramType = "path")
    @RequestMapping(value = "/get/{Id}",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> getAreaApi(@PathVariable("Id")Integer areaId){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("area",areaService.getArea(areaId));
        return modelMap;
    }
    @ApiOperation(value="创建区域", notes="根据 Area 对象创建区域")
    @ApiImplicitParam(name = "area", value = "区域详细实体 area", required = true, dataType = "Area")
    @RequestMapping(value = "/add",method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> addArea(Area area){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("success",areaService.addArea(area));
        return modelMap;
    }
    @ApiOperation(value="修改区域", notes="根据 Area 对象修改区域")
    @ApiImplicitParam(name = "area", value = "区域详细实体 area", required = true, dataType = "Area")
    @RequestMapping(value = "/edit",method = RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
    public Map<String,Object> editArea(Area area){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("success",areaService.addArea(area));
        return modelMap;
    }
    @ApiOperation(value="获取区域列表", notes="获取区域列表")
    @ApiImplicitParams ({
            @ApiImplicitParam(name = "pageNum", value = "第多少页", required = true, dataType = "Integer", paramType = "path"),
            @ApiImplicitParam(name = "pageSize", value = "每页取多少条记录", required = true, dataType = "Integer", paramType = "path")})
    @RequestMapping(value = "/all/{pageNum}/{pageSize}",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
    public Object findAllArea(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize){
        return areaService.findAllArea(pageNum,pageSize);
    }
    @GetMapping(value="/del/{areaId}")
    public Map<String,Object> deleteArea(@PathVariable Integer areaId){
        Map<String,Object> modelMap= new HashMap<String,Object>() ;
        modelMap.put("success",areaService.deleteArea(areaId));
        return modelMap;
    }
    @GetMapping("/test")
    @ApiIgnore//使用该注解忽略这个 API
    public String Test(){
        return "test";
    }
}

swagger 的相关注解

swagger 通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息的等等。

  •     @Api:修饰整个类,描述 Controller 的作用
  •     @ApiOperation:描述一个类的一个方法,或者说一个接口
  •     @ApiParam:单个参数描述
  •     @ApiModel:用对象来接收参数
  •     @ApiProperty:用对象接收参数时,描述对象的一个字段
  •     @ApiResponse:HTTP 响应其中 1 个描述
  •     @ApiResponses:HTTP 响应整体描述
  •     @ApiIgnore:使用该注解忽略这个 API
  •     @ApiError :发生错误返回的信息
  •     @ApiImplicitParam:一个请求参数
  •     @ApiImplicitParams:多个请求参数

启动 Spring Boot 程序,访问:http://localhost:8083/boot/swagger-ui.html,最终运行效果如下: