一.添加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
(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"));
}
}