天天看點

SpringBoot接口開發并內建SwaggerUI

一.添加maven依賴包

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--引入資料庫包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--引入swagger包-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.8.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.8.0</version>
        </dependency>
        <!--引入aop統一處理請求-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>
           

二.swaggerui配置檔案

package com.xxx.config;

import com.google.common.base.*;
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.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * swaggerui配置檔案
 *
 * @author fuyang
 * @date 2019/10/20
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket api(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .pathMapping("/")
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(Predicates.not(PathSelectors.regex("/error.*")))//不顯示swaggerui預設的接口位址
                .paths(PathSelectors.regex("/.*"))
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("swaggerui主标題名稱")
                .contact(new Contact("聯系人名稱","","聯系人郵箱位址"))
                .description("swaggerui說明")
                .version("1.0.0")
                .build();
    }

}

           

三.springboot配置

1.環境配置

server:
  port: 8086
  tomcat:
    uri-encoding: utf-8
  servlet:
    context-path: /luckymoney   -->接口通路的預設路徑
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/具體連結的庫名
    username: 資料庫主機名稱
    password: 資料庫密碼
  jpa:
    hibernate:
      ddl-auto: update           -->資料庫做更新操作
      show-sql: true             -->日志輸出sql執行語句
           

2.啟動檔案配置

說明:啟動檔案需與包名在同一級

package com.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LuckymoneyApplication {

    public static void main(String[] args) {
        SpringApplication.run(LuckymoneyApplication.class, args);
    }

}
           

四.接口開發

1.實體類開發(新增一張資料表)

package com.iot.domain;
import io.swagger.annotations.ApiParam;
import javax.persistence.*;
import java.math.*;

/**
 * 實體類開發
 *
 * @author fuyang
 * @date 2019/10/20
 */
@Entity
public class Luckymoney {
    //id為主鍵,自增
    @Id
    @GeneratedValue
    private Integer id;

    private BigDecimal money;

    private String producer;

    private String consumer;

    public Luckymoney() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public BigDecimal getMoney() {
        return money;
    }

    public void setMoney(BigDecimal money) {
        this.money = money;
    }

    public String getProducer() {
        return producer;
    }

    public void setProducer(String producer) {
        this.producer = producer;
    }

    public String getConsumer() {
        return consumer;
    }

    public void setConsumer(String consumer) {
        this.consumer = consumer;
    }

    @Override
    public String toString() {
        return "Luckymoney{" +
                "id=" + id +
                ", money=" + money +
                ", producer='" + producer + '\'' +
                ", consumer='" + consumer + '\'' +
                '}';
    }
}
           

2.自定義一個接口繼承JpaRepository,操作資料庫

在接口實作方法中,自動裝配該執行個體對象,然後調用jap下面的方法

建立資料:

repository.save(luckymoney)//傳入對象
           

更新資料:

repository.save(luckymoney)
           

查詢資料:

repository.findAll();
           

删除資料:

repository.deleteById(id);
           
package com.iot.repository;

import com.iot.domain.*;
import org.springframework.data.jpa.repository.JpaRepository;

/**自定義一個接口繼承JpaRepository接口,
 * 第一個參數是要操作資料庫中的某一個表名,
 * 第二個參數是主鍵的類型。
 * 定義好後即可使用Jpa中的方法完成對資料庫的基本操作。
 * @author luxiaojiu
 * @date 2019/10/20
 */
public interface LuckymoneyRepository extends JpaRepository<Luckymoney,Integer> {


}
           

3.基于RESTful開發api

SpringBoot接口開發并內建SwaggerUI

(1).get接口開發

/**
 * 擷取查詢操作
 * @return
 */
@GetMapping("/luckymoneys")
public List<Luckymoney> list(){
    return repository.findAll();
}
           

(2).post接口開發

/**
 * 建立操作
 * @return
 */
@PostMapping("/createLuckymoney")
public Luckymoney createLuckymoney (@RequestParam("producer") String producer, @RequestParam("money") BigDecimal money){
    Luckymoney luckymoney = new Luckymoney();
    luckymoney.setProducer(producer);
    luckymoney.setMoney(money);
    return repository.save(luckymoney);
}
           

(3).put接口開發

/**
 * 更新操作
 * @param id
 * @param consumer
 * @return
 */
@PutMapping("/updateLuckymoney/{id}")
public Luckymoney updateLuckymoney(@PathVariable("id") Integer id,@RequestParam("consumer") String consumer){
    Optional<Luckymoney> optional = repository.findById(id);
    if (optional.isPresent()){
        Luckymoney luckymoney = optional.get();
        luckymoney.setConsumer(consumer);
        return repository.save(luckymoney);
    }
    return null;
}
           

(4).delete接口開發

/**
     * 根據id删除對應資料
     * @param id
     * @return
     */
    @DeleteMapping("/deleteLuckymoney/{id}")
    public String deleteLuckymoney(@PathVariable("id") Integer id){
        repository.deleteById(id);
        return Integer.toString(id);
    }
           

五.接口文檔內建到Swagger UI

1.在接口開發類上方加入注解

@Api(tags = {"對應swagger ui組别名稱"})
           
@RestController
@Api(tags = {"紅包收發操作"})
public class LuckymoneyController {
}
           

2.在接口方法上方加入注解

@ApiOperation(value = "接口名稱",notes = "接口詳細說明")
           
@GetMapping("/selectLuckymoney/{id}")
    @ApiOperation(value = "通過主鍵id查詢紅包",notes = "擷取指定紅包資訊")
    public Luckymoney selectLuckymoney(@PathVariable("id") Integer id){
        return repository.findById(id).orElse(null);
    }
           

3.在接口請求參數前加入注解

@ApiParam(name = "請求參數名",value = "參數說明")
           
@GetMapping("/selectLuckymoney/{id}")
    @ApiOperation(value = "通過主鍵id查詢紅包",notes = "擷取指定紅包資訊")
    public Luckymoney selectLuckymoney(@ApiParam(name = "id",value = "主鍵ID")@PathVariable("id") Integer id){
        return repository.findById(id).orElse(null);
    }
           

4.運作swaggerUI,通路接口

(1).運作springboot啟動檔案

LuckymoneyApplication.java
           

(2).通路swaggerui連結

說明:端口号為springboot配置檔案中設定的端口号

http://localhost:8086/luckymoney/swagger-ui.html

六.SpringBoot中的事務處理

說明:一個流程中的步驟需同時成功或失敗,此時用事務實作;需要接口校驗的業務邏輯用事務實作

1.實作方式

@Service
public class LuckymoneyService {

    @Autowired
    private LuckymoneyRepository repository;

    /**
     * 資料庫事務,同時成功或失敗
     * ex:扣庫存 > 建立訂單 需同時成功或失敗
     */
    @Transactional
    public void createTwoLuckymoney(){
        Luckymoney luckymoney1 = new Luckymoney();
        luckymoney1.setProducer("大兄弟");
        luckymoney1.setMoney(new BigDecimal("520"));
        repository.save(luckymoney1);

        Luckymoney luckymoney2 = new Luckymoney();
        luckymoney2.setProducer("小兄弟");
        luckymoney2.setMoney(new BigDecimal("1314"));
        repository.save(luckymoney2);
    }
 }
           

2.在controller層調用

/**
     * 調用事務方法,同時建立兩個紅包
     */
    @PostMapping("/createTwo")
    public void createTwo(){
        service.createTwoLuckymoney();
    }
           

七.使用aop處理請求日志

package com.iot.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.*;

/**
 * 使用aop統一處理請求及傳回日志
 *
 * @author fuyang
 * @date 2019/10/28
 */
@Aspect
@Component
public class HttpAspect {

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
    
    //監控LuckymoneyController類下的所有方法
    @Pointcut("execution(public * com.iot.controller.LuckymoneyController.*(..))")
    public void log(){
    }

    /**
     * 輸出接口request資訊
     * @param joinPoint
     */
    @Before("log()")
    public void doBefore(JoinPoint joinPoint){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        //輸出url
        logger.info("url={}",request.getRequestURL());

        //輸出method
        logger.info("method={}",request.getMethod());

        //輸出請求ip
        logger.info("ip={}",request.getRemoteAddr());

        //輸出類方法
        logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());

        //輸出請求參數
        logger.info("args={}",joinPoint.getArgs());
    }

    //方法調用結束後需要輸出的資訊
    @After("log()")
    public void doAfter(){
        logger.info("接口調用結束!!!");
    }

    /**
     * 輸出接口響應資訊,将傳回的對象進行轉換字元串處理
     * @param object
     */
    @AfterReturning(returning = "object",pointcut = "log()")
    public void  doAfterReturning(Object object){
        logger.info("response={}",object.toString());

    }


}
           

八.接口響應字段格式設定

(1).定義接口具體傳回的字段

package com.iot.domain;

/**
 * 定義接口傳回的具體字段格式
 *
 * @author fuyang
 * @date 2019/10/29
 */
public class Result<T>{

    /**
     * 錯誤碼
     */
    private Integer code;

    /**
     * 提示資訊
     */
    private String msg;

    /**
     * 接口傳回的具體内容,泛型
     */
    private T data;

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
           

(2).為接口傳回的字段指派

調用Result類指派

package com.iot.utils;

import com.iot.domain.*;

/**
 * 為接口傳回的字段指派
 * 傳回Result.java對象
 * @author fuyang
 * @date 2019/10/29
 */
public class ResultUtil {
    public static Result success(Object object){
        Result result = new Result();
        result.setCode(0);
        result.setMsg("成功");
        result.setData(object);
        return result;
    }

    public static Result success(){
        return success(null);
    }

    public static Result error(Integer code,String msg){
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}
           

九.異常處理

說明:對抛出的異常進行處理

(1).定義一個枚舉類,管理接口傳回的code和message,

調用方式:ResultEnum.PRODUCER_ERROR

備注:接口傳回的格式為

{
  "code": 102,
  "msg": "未查詢到結果",
  "data": null
}
           
package com.iot.enums;

/**
 * 為接口傳回的code和message定義枚舉
 * 友善統一管理
 * @author fuyang
 * @date 2019/10/31
 */
public enum ResultEnum {
    UNKNOW_ERROR(-1,"未知錯誤"),
    SUCCESS(0,"成功"),
    PRODUCER_ERROR(100,"發送人錯誤"),
    AMOUNT_ERROR(101,"發送金額錯誤"),
    PARAM_ERROR(102,"未查詢到結果");

    private Integer code;
    private String message;

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    ResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
           

(2).自定義異常類(解決抛出的異常按照規定的格式傳回)

package com.iot.exception;

import com.iot.enums.*;

/**
 * 部分異常的情況不滿足Result.java定義的格式,需單獨處理
 * 自定義異常類,需傳入code和message
 * @author fuyang
 * @date 2019/10/30
 */
public class LuckymoneyException extends RuntimeException{

    private Integer code;

    public LuckymoneyException(ResultEnum resultEnum) {
        super(resultEnum.getMessage());
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}
           

(3).定義一個異常捕獲類(如果捕獲到的類是自定義的異常類,則傳回自定義的響應格式)

package com.iot.handle;

import com.iot.domain.*;
import com.iot.exception.*;
import com.iot.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 定義一個異常捕獲類
 * 對捕獲到的類進行字段傳回值的處理
 * @author fuyang
 * @date 2019/10/30
 */
@ControllerAdvice
public class ExceptionHandle {

    private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result handle(Exception e){
        LuckymoneyException luckymoneyException = (LuckymoneyException) e;
        //如果捕獲到的異常類是LuckymoneyException抛出的則走下面判斷
        if (e instanceof LuckymoneyException){
            return ResultUtil.error(luckymoneyException.getCode(),luckymoneyException.getMessage());
        }else {
            logger.error("【系統異常】{}",e);
            return ResultUtil.error(-1,"未知錯誤");
        }

    }
}
           

(4).在事務中調用自定義異常類LuckymoneyException,參數code/message直接讀取枚舉

/**
     * 校驗紅包發送人
     * @param id
     * @throws Exception
     */
    public void getMoney(Integer id) throws Exception{
        Luckymoney luckymoney = repository.findById(id).get();
        String producer = luckymoney.getProducer();
        if (producer.equals("echo")){
            throw new LuckymoneyException(ResultEnum.PRODUCER_ERROR);
        }else if (producer.equals("luke")){
            throw new LuckymoneyException(ResultEnum.AMOUNT_ERROR);
        }

    }
           

十.基于junit單元測試

(1).對service測試

選中service中待測試的方法名,右鍵go to ---> Test ---> create new test ---> 勾選待測試的方法 --- ok

package com.iot.service;

import com.iot.domain.*;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * 單元測試
 * 基于事務中的校驗
 * @author fuyang
 * @date 2019/11/10
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class LuckymoneyServiceTest {

    @Autowired
    private LuckymoneyService luckymoneyService;

    @Test
    public void findOneTest(){
        Luckymoney luckymoney = luckymoneyService.findOne(30);
        Assert.assertEquals("echo",luckymoney.getProducer());
    }

}
           

(2).對controller api測試

選中controller中待測試的方法名,右鍵go to ---> Test ---> create new test ---> 勾選待測試的方法 --- ok

package com.iot.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class LuckymoneyControllerTest {

    @Autowired
    private MockMvc mvc;

    //校驗傳回的狀态碼為200,内容為iot
    @Test
    public void list() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/luckymoneys"))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.content().string("iot"));
    }
}