通过feign获取第三方接口,将结果映射成dto,不过dto里面的对象的属性接收的值命名可能不规范(全是大写等,不是驼峰命令等方式),所以才会用vo来接收dto的值。
如果只是对象copy,可以使用BeanUtils.copyProperties进行对象之间的属性赋值(浅拷贝)
但是如果对象里面还有对象和集合之类的,这样就copy失败了,这里就可以采用Mapstruct工具类进行深拷贝。
1.引入相关依赖(pom.xml)
org.mapstruct
mapstruct-jdk8
${org.mapstruct.version}
org.mapstruct
mapstruct-processor
${org.mapstruct.version}
2.引入plugin(pom.xml)
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
1.8
3.新增mapper接口SourceTargetMapper
package com.frank.hello.mapper;
import com.frank.hello.dto.DataResponse;
import com.frank.hello.dto.Teacher;
import com.frank.hello.vo.DataResponseVO;
import com.frank.hello.vo.TeacherVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author 小石潭记
* @date 2020/7/2 21:39
* @Description: ${todo}
*/
@Mapper(componentModel = "spring")
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
TeacherVO toTarget(Teacher source);
// 如果需要转多个,则再定义一个方法,将源数据和目标数据修改即可
// TargetData toTargetData(SourceData sourceData);
}
4.调用接口方式,传入源数据,生成目标数据
SoucrceData data = SourceTargetMapper.MAPPER.toTarget(targetData);
集成mapstruct可能遇到的坑
1)查看资料如果引入swagger,在引入mapstruct,需要在swagger依赖下添加
io.springfox
springfox-swagger2
2.2.2
mapstruct
org.mapstruct
io.springfox
springfox-swagger-ui
2.2.2
mapstruct
org.mapstruct
2)mapstruct和lombok同时引入,可能会出现生成不了get、set方法(compile生成接口的实现类),在plugin里面添加
注意lombok的版本不要太低,我这里是:
1.8
jdt_apt
1.3.0.Final
1.18.12
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
1.8
3)最后一点我被坑惨了。
注意DTO和VO里面的属性名称一定要一致,返回的对象可以不一样,但是属性名一定要一致,图中的list里面的对象的属性名也要保持一致,不然compile不会生成对应的get、set这样拷贝的对象就不正确,没有拷贝完所有属性,这个地方我被坑惨了(实际项目中DTO对象嵌套的层数太多了,没有逐一去查看属性是否一致。)
4)如果对象不复杂,可以通过下面的方式进行mapping一一映射
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @Mapping 用来定义属性复制规则 source 指定源对象属性 target指定目标对象属性
*
* @param user 这个参数就是源对象,也就是需要被复制的对象
* @return 返回的是目标对象,就是最终的结果对象
*/
@Mappings({
@Mapping(source = "id", target = "userId"),
@Mapping(source = "username", target = "name"),
@Mapping(source = "role.roleName", target = "roleName")
})
UserRoleDto toUserRoleDto(User user);
目录结构:
application.properties文件暂未配置内容
Swagger2
package com.frank.hello.config;
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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author 小石潭记
* @date 2020/7/2 22:11
* @Description: ${todo}
*/
@Configuration
@EnableSwagger2
// http://localhost:8080/swagger-ui.html
public class Swagger2 {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.frank.hello"))//扫描接口的包
.build();
}
public ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("spring boot利用swagger构建api文档")
.description("简单优雅的rest风格")
.termsOfServiceUrl("http://localhost:8080")//文档遵循的开发协议的展现网址
.version("1.0")//版本
.build();
}
}
Animal
package com.frank.hello.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭记
* @date 2020/7/2 21:11
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Animal {
@JsonProperty("NAME")
private String name;
@JsonProperty("AGE")
private int age;
}
DataResponse
package com.frank.hello.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭记
* @date 2020/7/2 21:15
* @Description: ${todo}
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class DataResponse {
@JsonProperty("CODE")
private int code;
@JsonProperty("MESSAGE")
private String message;
@JsonProperty("DATA")
private Teacher data;
}
Student
package com.frank.hello.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭记
* @date 2020/7/2 21:10
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
@JsonProperty("ID")
private int id;
@JsonProperty("NAME")
private String name;
@JsonProperty("ADDRESS")
private String address;
@JsonProperty("ANIMALS")
private List animals;
}
Teacher
package com.frank.hello.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭记
* @date 2020/7/2 21:10
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
@JsonProperty("ID")
private int id;
@JsonProperty("NAME")
private String name;
@JsonProperty("ADDRESS")
private String address;
@JsonProperty("STUDENTS")
private List students;
}
SourceTargetMapper(核心配置,compile项目之后,会生成该接口的实现类)
package com.frank.hello.mapper;
import com.frank.hello.dto.DataResponse;
import com.frank.hello.dto.Teacher;
import com.frank.hello.vo.DataResponseVO;
import com.frank.hello.vo.TeacherVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author 小石潭记
* @date 2020/7/2 21:39
* @Description: ${todo}
*/
@Mapper(componentModel = "spring")
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
TeacherVO toTarget(Teacher dataResponse);
// 如果需要转多个,则再定义一个方法,将源数据和目标数据修改即可
// TargetData toTargetData(SourceData sourceData);
}
AnimalVO
package com.frank.hello.vo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭记
* @date 2020/7/2 21:11
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "AnimalVO", description = "动物类")
public class AnimalVO {
@ApiModelProperty(value = "名字",
name = "name",
example = "小花")
private String name;
@ApiModelProperty(value = "年级",
name = "age",
example = "1")
private int age;
}
DataResponseVO
package com.frank.hello.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @author 小石潭记
* @date 2020/7/2 21:15
* @Description: ${todo}
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Accessors(chain = true) //链式风格,在调用set方法时,返回这个类的实例对象
@ApiModel(value = "DataResponseVO", description = "返回的结果类")
public class DataResponseVO {
@ApiModelProperty(value = "返回的code码",
name = "code",
example = "200")
private int code;
@ApiModelProperty(value = "返回的信息",
name = "message",
example = "success")
private String message;
@ApiModelProperty(value = "返回的数据",
name = "data",
example = "返回的数据")
private TeacherVO data;
}
StudentVO
package com.frank.hello.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭记
* @date 2020/7/2 21:10
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "StudentVO", description = "学生类")
public class StudentVO {
@ApiModelProperty(value = "学号",
name = "id",
example = "1001")
private int id;
@ApiModelProperty(value = "学生的姓名",
name = "name",
example = "张三")
private String name;
@ApiModelProperty(value = "学生地址",
name = "address",
example = "成都")
private String address;
@ApiModelProperty(value = "学生所拥有的动物",
name = "animals",
example = "学生所拥有的动物")
private List animals;
}
TeacherVO
package com.frank.hello.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小石潭记
* @date 2020/7/2 21:10
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "TeacherVO", description = "老师类")
public class TeacherVO {
@ApiModelProperty(value = "老师的编号",
name = "id",
example = "1001")
private int id;
@ApiModelProperty(value = "老师的姓名",
name = "name",
example = "张老师")
private String name;
@ApiModelProperty(value = "地址",
name = "address",
example = "成都")
private String address;
@ApiModelProperty(value = "老师所教的学生",
name = "students",
example = "老师所教的学生")
private List students;
}
HelloController
package com.frank.hello.web;
import com.frank.hello.dto.Animal;
import com.frank.hello.dto.DataResponse;
import com.frank.hello.dto.Student;
import com.frank.hello.dto.Teacher;
import com.frank.hello.mapper.SourceTargetMapper;
import com.frank.hello.vo.DataResponseVO;
import com.frank.hello.vo.TeacherVO;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* @author 小石潭记
* @date 2020/7/2 21:12
* @Description: ${todo}
*/
@RestController
@Api(value = "HelloController", description = "主页")
public class HelloController {
@GetMapping("/index")
@ApiOperation(value = "获取接口信息",notes = "获取接口信息",tags = "DataResponseVO",httpMethod = "GET")
@ApiResponses({//方法返回值的swagger注释
@ApiResponse(code = 200,message = "成功",response = DataResponseVO.class),
@ApiResponse(code = 400,message = "用户输入错误",response = DataResponseVO.class),
@ApiResponse(code = 500,message = "系统内部错误",response = DataResponseVO.class)
})
public DataResponseVO index(){
List animals = new ArrayList<>();
animals.add(new Animal("小一", 1));
animals.add(new Animal("小二", 2));
animals.add(new Animal("小三", 3));
List students = new ArrayList<>();
students.add(new Student(1, "小明", "成都", animals));
Teacher teacher = new Teacher(1, "小梅沙", "四川", students);
// 这里模拟从第三方接口获取的数据
DataResponse dataResponse = new DataResponse(200, "成功", teacher);
// 这里直接将上面的数据拷贝变成DataResponseVO,这里注意一下先compile一下整个项目
TeacherVO responseVo = SourceTargetMapper.MAPPER.toTarget(dataResponse.getData());
DataResponseVO dataResponseVO = new DataResponseVO();
dataResponseVO.setCode(dataResponse.getCode()).setMessage(dataResponse.getMessage()).setData(responseVo);
return dataResponseVO;
}
}
HelloApplication
package com.frank.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
编译项目之后,会自动生成mapper的实现类。
http://localhost:8080/swagger-ui.html 这里可以查看对应接口的请求参数、返回对象等信息。
项目地址