Java 杂谈java所有基础知识框架原理

POJO转VO,让你不再痛苦

2018-12-20  本文已影响10人  jerrik

在开始之前,我们来说一说传统的代码开发风格:
        首先,我们要有dao、service、controller三层结构。dao用来操作DB,service层封装具体的业务逻辑,Controller层面向前端页面。复杂点的项目会引入一个bo层,也就是dao和service的中间层。比如单表的一些相对复杂的操作封装在bo里,service层只处理实体与实体之间的关系。
        当我们需要在前端页面展示多个实体数据的时候,我们是这样做的:
1.新建一个vo对象
2.vo对象包含我们所需要返回的实体属性
3.然后通过各类properties copy工具来复制属性
过程非常的无聊、甚至痛苦,但是又毫无办法。
得益于有了mapStruct这个组件,让pojo转vo不在痛苦。

一、mapStruct环境准备

导入依赖,一般是结合lombok一起使用,不然略显单调(lombok的使用说明可以查看之前文章):

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>

其实还有另外一种方式,就是基于maven插件的做法,就可以不用导入mapstruct-processor了。

     <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-compiler-plugin</artifactId>
           <version>3.5.1</version>
           <configuration>
               <source>${java.version}</source>
               <target>${java.version}</target>
               <encoding>${java.encoding}</encoding>
               <annotationProcessorPaths>
                   <path>
                       <groupId>org.mapstruct</groupId>
                       <artifactId>mapstruct-processor</artifactId>
                       <version>${org.mapstruct.version}</version>
                   </path>
               </annotationProcessorPaths>
           </configuration>
    </plugin>

但是一般还是使用的第一种方式。
然后在idea->settings->annotation processors->启用注解处理


二、使用

准备一个Goods 对象和User对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Goods {
    private Integer id;
    private String descs;
    private double prices;
    private String name;
    private String about;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class User {
    private int userId;
    private String userName;
    private int age;
    private String description;
}

现在我需要拿到商品和用户信息,只需要新建一个DTO--GoodsInfoDTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
public class GoodsInfoDto {
    private Integer goodsId;
    private String descs;
    private double goodsPrices;
    private String goodsName;
    private String about;
    private int userId;
    private String userName;
    private int age;
    private String userDescs;
}

然后一个一个的set属性吗?NO,来看mapStruct怎么做的。
新建一个converter类,名字随意,我这里叫GoodsInfoConverter

@Mapper
public interface GoodsInfoConverter {
    GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);
    @Mappings({@Mapping(source = "goods.id", target = "goodsId"),
            @Mapping(source = "goods.prices", target = "goodsPrices"),
        @Mapping(source = "goods.name",target = "goodsName"),
        @Mapping(source = "user.description",target = "userDescs"),
    }
    )
    public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user);
}

其中的GoodsInfoConverter INSTANCE = Mappers.getMapper(GoodsInfoConverter.class);必不可少,类似于一个SPI接口,就可以让你再客户端通过GoodsInfoConverter.INSTANCE获取到它的实例。
下面的@Mappings就是对象-DTO之间的映射关系,这里要转换goodsuser对象到DTO,所以传入了两个参数。其中的source是需要转换的对象属性,如果有多个对象需要转换,则需要在前面加入对应的对象名,例如goods.id,用过hibernate的同学会觉得格外小清新~.其中的target是DTO中的属性名,如果需要转换的对象和DTO属性名相同,则不需要配置。还有一些其它参数,我就不一一介绍了。

最重要的一步,就是编译,mvn clean一下:
你会发现在target目录的generated-sources下会生成一个GoodsInfoConverterImpl:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2018-12-20T15:44:31+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_181 (Oracle Corporation)"
)
public class GoodsInfoConverterImpl implements GoodsInfoConverter {

    @Override
    public GoodsInfoDto goods2GoodsInfoDto(Goods goods, User user) {
        if ( goods == null && user == null ) {
            return null;
        }

        GoodsInfoDto goodsInfoDto = new GoodsInfoDto();

        if ( goods != null ) {
            goodsInfoDto.setGoodsPrices( goods.getPrices() );
            goodsInfoDto.setGoodsName( goods.getName() );
            goodsInfoDto.setGoodsId( goods.getId() );
            goodsInfoDto.setDescs( goods.getDescs() );
            goodsInfoDto.setAbout( goods.getAbout() );
        }
        if ( user != null ) {
            goodsInfoDto.setUserDescs( user.getDescription() );
            goodsInfoDto.setUserId( user.getUserId() );
            goodsInfoDto.setUserName( user.getUserName() );
            goodsInfoDto.setAge( user.getAge() );
        }

        return goodsInfoDto;
    }
}

完全是自动生成的,你不需要管。当我们mvn打包的时候会自动将该文件打包进去。
客户端怎么使用呢?来看一下:

        Goods goods = new Goods();
        goods.setId(3);
        goods.setDescs("飞机");
        goods.setPrices(32000.0D);
        goods.setName("无人机");

        User user = new User();
        user.setUserId(13581);
        user.setUserName("jerrik");
        user.setAge(25);
        user.setDescription("帅小伙");

        GoodsInfoDto goodsInfoDto = GoodsInfoConverter.INSTANCE.goods2GoodsInfoDto(goods,user);
        System.out.println(goodsInfoDto);

//GoodsInfoDto(goodsId=3, descs=飞机, goodsPrices=32000.0, goodsName=无人机, about=null, userId=13581, userName=jerrik, age=25, userDescs=帅小伙)

是不是很简单,而且省去了很多事,比bean copy的性能也要好。完全是基于set来实现的。

三、注意事项

mapStruct集成lombok时,lombok的版本设置成新版本吧。反正笔者1.16.8的lombok会报错,提示Mappings找不到映射的属性,1.16.18是可以编译通过的。如果你觉得这些还不够用,可以去查看官网,谢谢~

上一篇下一篇

猜你喜欢

热点阅读