Springboot使用Mapstruct拷贝对象,集成swagger2

实际需求

通过feign获取第三方接口,将结果映射成dto,不过dto里面的对象的属性接收的值命名可能不规范(全是大写等,不是驼峰命令等方式),所以才会用vo来接收dto的值。

如果只是对象copy,可以使用BeanUtils.copyProperties进行对象之间的属性赋值(浅拷贝)

但是如果对象里面还有对象和集合之类的,这样就copy失败了,这里就可以采用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.引入plugin(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.新增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依赖下添加<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方法(compile生成接口的实现类),在plugin里面添加<annotationProcessorPaths>如下文所示</annotationProcessorPaths>

注意lombok的版本不要太低,我这里是:

<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里面的属性名称一定要一致,返回的对象可以不一样,但是属性名一定要一致,图中的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<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);
    }

}

编译项目之后,会自动生成mapper的实现类。

http://localhost:8080/swagger-ui.html   这里可以查看对应接口的请求参数、返回对象等信息。

项目地址

猜你喜欢

转载自blog.csdn.net/qq_33371766/article/details/107114031