実際の需要
feignを介してサードパーティのインターフェイスを取得し、結果をdtoにマップしますが、dtoのオブジェクトのプロパティが受け取る値の名前は不規則である可能性があります(すべて大文字など、キャメルケースコマンドなどではありません)。 、したがって、voはdtoの値を受け取るために使用されます。
オブジェクトのコピーだけの場合は、BeanUtils.copyPropertiesを使用して、オブジェクト間でプロパティを割り当てることができます(浅いコピー)
ただし、オブジェクトとコレクションがオブジェクトにあり、コピーが失敗した場合は、Mapstructツールクラスを使用してディープコピーを実行できます。
Mapstructの実装手順
1.関連する依存関係を導入します(pom.xml)
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
2.プラグイン(pom.xml)を導入します
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!--<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>-->
</configuration>
</plugin>
3.新しいマッパーインターフェイス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の依存関係の下に<exclusions>を追加する必要があります</ exclusions>
<!-- 引入swagger包 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
<exclusions>
<exclusion>
<artifactId>mapstruct</artifactId>
<groupId>org.mapstruct</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
<exclusions>
<exclusion>
<artifactId>mapstruct</artifactId>
<groupId>org.mapstruct</groupId>
</exclusion>
</exclusions>
</dependency>
2)mapstructとlombokが同時に導入された場合、getメソッドとsetメソッド(コンパイル生成インターフェイスの実装クラス)が生成されない場合があります。以下に示すように、プラグインに<annotationProcessorPaths>を追加します</ annotationProcessorPaths>
ロンボクのバージョンは低すぎてはいけないことに注意してください、ここに私はいます:
<properties>
<java.version>1.8</java.version>
<m2e.apt.activation>jdt_apt</m2e.apt.activation>
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
<lombok.version>1.18.12</lombok.version>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!--<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>-->
</configuration>
</plugin>
3)最後に悲惨だった。
DTOとVOの属性名は同じである必要があり、返されるオブジェクトは異なる場合がありますが、属性名は同じである必要があり、図のリスト内のオブジェクトの属性名も一貫している必要があります。そうでない場合はコンパイルします。対応するgetを生成しません。setによってコピーされたオブジェクトが正しくありません。すべてのプロパティをコピーしませんでした。この場所で惨めでした(実際のプロジェクトにはDTOオブジェクトのレイヤーが多すぎて、プロパティは一貫しています。)
4)オブジェクトが複雑でない場合は、次の方法で1対1のマッピングを実行できます。
/**
* 这个方法就是用于实现对象属性复制的方法
*
* @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<Animal> 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<Student> 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<AnimalVO> 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<StudentVO> 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<Animal> animals = new ArrayList<>();
animals.add(new Animal("小一", 1));
animals.add(new Animal("小二", 2));
animals.add(new Animal("小三", 3));
List<Student> 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);
}
}
プロジェクトのコンパイル後、マッパー実装クラスが自動的に生成されます。
http:// localhost:8080 / swagger-ui.html ここでは、対応するインターフェースのリクエストパラメータを表示したり、オブジェクトやその他の情報を返したりできます。