1)添加依赖,我使用3.0.0版本时会出现swagger-ui页面404的问题,所以改成2.9.2,使用默认版本swagger-model会出现判空异常。
<!-- swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2)swagger注入配置
要分多少类别就注入多少个Docket,enable使用配置文件,根据运行环境来决定是否启用,一般生产环境不要开启swagger。
package com.example.jiakao.common.config;
import com.example.jiakao.common.prop.ProjProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
@Configuration
@EnableSwagger2
public class SwaggerCfg {
private static final String VERSION = "1.0.0";
@Autowired
private ProjProperties properties;
@Bean
public Docket docket(){
return basicDocket("all")
.apiInfo(apiInfo("接口文档","详细接口文档"))
.select()
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.build();
}
@Bean
public Docket dictDocket(){
return basicDocket("dict","/(dict.*)")
.apiInfo(apiInfo("dict接口文档","dict详细接口文档"));
}
private ApiInfo apiInfo(String title, String description){
ApiInfoBuilder builder = new ApiInfoBuilder();
builder.title(title);
builder.description(description);
builder.version(VERSION);
return builder.build();
}
private Docket basicDocket(String name){
Parameter token = new ParameterBuilder()
.name("Token")
.description("用户登录令牌")
.parameterType("header")
.modelRef(new ModelRef("String"))
.build();
return new Docket(DocumentationType.SWAGGER_2)
.ignoredParameterTypes(HttpSession.class, HttpServletRequest.class, HttpServletResponse.class)
.groupName(name)
.enable(properties.getSwagger());
// .globalOperationParameters(List.of(token));
}
private Docket basicDocket(String name, String regex){
return basicDocket(name)
.select()
.paths(PathSelectors.regex(regex))
.build();
}
}
3)常用注解
4)项目结构完善
数据库po对象应该与vo对象分离开,对于swagger的配置应该标注在vo对象上,而不应该与po对象混在一起。
po对象,用于数据库操作
package com.example.jiakao.pojo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import javax.persistence.*;
@Entity
@Data
@TableName("plate_region")
@Table(name="plate_region")
public class PlateRegionPo {
@Id
@TableId(type = IdType.AUTO)
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column( columnDefinition = "int4" )
private Integer id;
@Column( columnDefinition = "varchar(64)" )
private String name;
@Column( columnDefinition = "varchar(64)" )
private String plate;
@Column( columnDefinition = "int4 default 0 " )
private Integer parentId;
}
vo对象,用于保存时传参
package com.example.jiakao.pojo.vo.req.save;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("保存一个省份或者城市信息的参数")
public class PlateRegionReqVo {
@ApiModelProperty(value = "省份或者城市的id")
private Integer id;
@ApiModelProperty(value = "省份或者城市的名称", required = true)
private String name;
@ApiModelProperty(value = "省份或者城市的代号", required = true)
private String plate;
@ApiModelProperty(value = "城市所属省份的id,省份为0,默认值为0", required = true)
private Integer parentId = 0;
}
因为将将参数与po对象分开了,所以会射涉及大量vo转po,这里可以使用mapstruct来简化转化过程。
<!-- 对象转换,编译期间存在-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.3.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.3.Final</version>
<scope>provided</scope>
</dependency>
在做vo和po之间相互转换时,我们有时候会有将一个类型转为其他类型或者字段名不一致的情况,这时候我们可以使用MapStrcut的Mapping注解。
@Mapping(source = "createTime",target="createTime",qualifiedBy = MapStructFormatter.Date2Millis.class)
qualifiedBy属性指定类型转换使用的方法,该方法拥有一个Date2Millis注解。注解和转换方法定义如下。规定注解必须要使用@Qualifier注解标注。@Retention(RetentionPolicy.CLASS)代表该注解只在编译期间起作用。@Target(ElementType.METHOD)代表该注解应标注在方法上。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Date;
public class MapStructFormatter {
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Date2Millis {
}
@Date2Millis
public static Long date2millis(Date date){
if(date == null) return null;
return date.getTime();
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Millis2Date {
}
@Millis2Date
public static Date millis2date(Long timeStamp){
if(timeStamp == null) return null;
return new Date(timeStamp);
}
}
使用方式,用不了泛型。@Mapper(uses = {MapStructFormatter.class})代表应在哪个类中寻找转换方法。qualifiedBy = MapStructFormatter.Date2Millis.class)代表应该使用被Date2Millis注解标注的方法进行类型转换。
package com.example.jiakao.common.mapStruct;
import com.example.jiakao.pojo.entity.*;
import com.example.jiakao.pojo.vo.list.UserVo;
import com.example.jiakao.pojo.vo.req.save.*;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(uses = {
MapStructFormatter.class
})
public interface MapStructs {
// 接口中的属性只能是public static,可以省略
MapStructs INSTANCE = Mappers.getMapper(MapStructs.class);
@Mapping(source = "createTime",target="createTime",qualifiedBy = MapStructFormatter.Date2Millis.class)
@Mapping(source = "updateTime",target="updateTime",qualifiedBy = MapStructFormatter.Date2Millis.class)
UserVo po2vo(UsersPo po);
ExamPlacePo reqVo2po(ExamPlaceReqVo vo);
PlateRegionPo reqVo2po(PlateRegionReqVo vo);
ExamCoursePo reqVo2po(ExamCourseReqVo po);
UsersPo reqVo2po(UsersReqVo po);
RolesPo reqVo2po(RolesReqVo po);
ResourcePo reqVo2po(ResourceReqVo po);
}
同时我们可以抽离一个BaseController的抽象类,实现一些基础接口。
package com.example.jiakao.controller;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.jiakao.common.constant.ResultCode;
import com.example.jiakao.exception.http.ArgumentsException;
import com.example.jiakao.common.util.Vos;
import com.example.jiakao.pojo.vo.json.JsonVo;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Arrays;
/**
*
* @param <Po> 数据库entity类
* @param <ReqVo> pojo.vo.req.save中的类,用于插入数据库的请求参数,非entity
*/
public abstract class BaseController<Po, ReqVo> {
protected abstract IService<Po> getService();
protected abstract Po reqVo2Po(ReqVo reqVo);
@ApiOperation("删除一条数据或多条数据")
@PostMapping("/remove")
public JsonVo remove(
@ApiParam(value = "一个或多个id,以逗号分割",required = true)
@RequestParam String id){
String[] ids = id.split(",");
if(getService().removeByIds(Arrays.asList(ids))){
return Vos.ok();
} else{
throw new ArgumentsException(ResultCode.ARGUMENTS_ERROR);
}
}
@ApiOperation("保存一条数据")
@PostMapping("/save")
public JsonVo save(@RequestBody ReqVo vo) {
Po entity = reqVo2Po(vo);
if(getService().saveOrUpdate(entity)){
return Vos.ok();
} else{
throw new ArgumentsException(ResultCode.ARGUMENTS_ERROR);
}
}
}
目录结构