美文网首页JavaJava 杂谈java成长之路
告别set和get,两大利器轻松搞定model转换

告别set和get,两大利器轻松搞定model转换

作者: Jackie_Zheng | 来源:发表于2018-09-15 20:03 被阅读334次

    场景一:一般我们遇到需要新建model,常规做法就是创建一个类,老老实实的定义好model中的所有属性,一般来说属性对应的set方法和get方法都是少不了的,有时候还需要toString甚至equals和hashCode方法。

    现在的IDE已经很成熟了,一般不会手写set和get方法,采用IDE自带的快捷方式自动生成居多。如下图所示

    image

    该方式相对手写方法来说,效率已经有了很大的提升,但还是有进一步的提升空间(下文会介绍)。而且该种方式维护性较差,当需要修改某个属性名称或者属性类型时,对应的set和get方法以及toString都需要调整。

    场景二:大部分时候,我们都是基于当前流行的微服务架构和SSM(Spring+Spring MVC + Mybatis)框架进行开发,这时候我个人经常遇到一个问题就是model的转换问题。

    不同层会有不同的model,比如DAO层的model,service层的model,对外API接口的model,还有更上层的controller层的model以及提供给前端的View model。

    为了能够是接口正常调用,我们不得不处理这些model的转换,没有一个称手的工具,我们只能手写转换类,通过一个又一个的set和get方法来完成model的转换。

    有时候,我们在测试接口的时候发现有些属性没有值,调试半天才发现,是因为其中一个属性忘记写set方法了。我们明知道这些工作并不需要太多的思考,但是却不得不小心翼翼的对待。

    那么,是否有更加优雅的处理方式,请看下文介绍

    Lombok

    这是一个插件,能够很好的解决场景一中的难题

    下载安装

    我用的IDE是Intellij idea,可以在Preferences->Plugins中找到相应的插件安装并重启即可。

    image

    该插件的实现已经放在github上,有兴趣可以到https://github.com/mplushnikov/lombok-intellij-plugin查看

    lombok使用

    添加jar包依赖

    在你需要的项目的pom文件中添加如下的依赖

    
    <dependency>  <groupId>org.projectlombok</groupId>  <artifactId>lombok</artifactId>  <version>1.16.18</version>  </dependency>
    
    
    编写model
    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    import java.util.Date;
    
    public class AuthorDTO {   private String name;
    
        private Date birthday;
    }
    
    
    添加需要的注解

    Lombok可以通过注解的方式实现你需要添加的方法,比如你需要添加这些属性对应的set方法,那么只要在model类上添加注解@Setter即可,相应的,如果需要get方法添加@Getter

    此外还有@ToString@NoArgsConstructor@AllArgsConstructor等方便使用的注解。

    事实上,我们真的只需要这样添加注解的方式,就能够实现轻松调用set和get方法的需要。这样,以后如果model的属性有改变,我们只需要直接改相应的属性即可,不再需要做任何一点多余的操作。

    将注解还原为具体方法

    Lombok为我们提供可以将对应注解还原为对应方法的功能。

    点击Refactor->Delombok选择想还原的方法即可

    image

    是不是很好用?

    Orika

    Orika是一个简单快速的model拷贝框架。

    Orika使用

    添加jar包依赖

    在需要使用的项目的pom文件中添加如下依赖

    
    <dependency>  <groupId>ma.glasnost.orika</groupId>  <artifactId>orika-core</artifactId>  <version>1.5.2</version>  </dependency>
    
    
    创建两个需要转换的model

    BookEntity

    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    import java.util.Date;
    
    @Setter @Getter @AllArgsConstructor @NoArgsConstructor public class BookEntity {   private String bookName;
    
        private String authorName;
    
        private Date authorBirthday;
    
        private String bookInformation;
    
        private Integer type;
    
    } 
    
    

    BookDTO

    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    import lombok.Getter;
    import lombok.Setter;
    
    @Setter @Getter public class BookDTO {   private String bookName;
    
        private AuthorDTO author;
    
        private BookType bookType;
    
        private BookInfo bookInfo;
    }
    
    

    备注:这里的AuthorDTO、BookType和BookInfo如下

    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    import lombok.*;
    
    import java.util.Date;
    
    @Setter @Getter @ToString @NoArgsConstructor @AllArgsConstructor public class AuthorDTO {   private String name;
    
        private Date birthday;
    } 
    
    
    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    public enum BookType {
     NOVEL(1),
        ESSAY(2);
    
        private int value;
    
        BookType(int value) {
     this.value = value;
        }   public static BookType getBookType(int value) {
     BookType bookType = null;
    
            switch (value) {
     case 1:
                    bookType = NOVEL;
                    break;
                case 2:
                    bookType = ESSAY;
                    break;
                default:
                    break;
            }
     return bookType;
        }   public int getValue() {
     return value;
        } } 
    
    
    
    package com.jackie.wowjava.best.practice.java.orika.model;
    
    import lombok.Getter;
    import lombok.Setter;
    
    @Setter @Getter public class BookInfo {   private String ISBN;
    
        private int page;
    }
    
    

    你没看错,就是BookDTO和BookEntity这两个model,需要相互转换,Orika可以帮你搞定,具体看下面是如何实现的。

    model转换

    我们看到两个model中包含了多种情况

    • 属性名称完全一样的,比如bookName

    • 一个属性对应一个对象的,BookDTO中的AuthorDTO对应BookEntity中的authorName以及authorBirthday

    • 枚举类型的,比如BookEntity的type和BookDTO的BookType

    • JSON类型的,比如BookEntity的bookInformation和BookDTO的bookInfo

    1、属性名称完全一样的属性拷贝

    新建测试类OrikaTest

    
    package com.jackie.wowjava.best.practice.java.orika;
    
    import com.alibaba.fastjson.JSON;
    import com.jackie.wowjava.best.practice.java.orika.model.BookDTO;
    import com.jackie.wowjava.best.practice.java.orika.model.BookEntity;
    import com.jackie.wowjava.best.practice.java.orika.model.BookInfo;
    import com.jackie.wowjava.best.practice.java.orika.model.BookType;
    import ma.glasnost.orika.MapperFactory;
    import ma.glasnost.orika.MappingContext;
    import ma.glasnost.orika.converter.BidirectionalConverter;
    import ma.glasnost.orika.impl.DefaultMapperFactory;
    import ma.glasnost.orika.metadata.Type;
    
    import java.time.LocalDate;
    import java.time.Month;
    import java.time.ZoneId;
    import java.util.Date;
    
    public class OrikaTest {   private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    
        public static void main(String[] args) {   BookEntity bookEntity = new BookEntity(
     "银河系漫游指南",
                    "道格拉斯·亚当斯",
                    Date.from(LocalDate.of(1952, Month.MARCH, 11).atStartOfDay(ZoneId.systemDefault()).toInstant()),
                    "{\"ISBN\": \"9787532754687\", \n \"page\": 279\n }",
                    1);
    
            BookDTO bookDTO = mapperFactory.getMapperFacade().map(bookEntity, BookDTO.class);
    
            System.out.println(JSON.toJSONString(bookDTO));
        } } 
    
    

    运行结果如下

    
    {"bookName":"银河系漫游指南"}
    
    

    没错,只有名称相同的属性被转换了。

    从代码来看,我们不需要做任何特殊化处理就能做到这一点,因为Orika默认就是按照名称相同就拷贝进行处理的。

    2、一个属性对应一个对象的属性拷贝

    这里我们是想BookDTO中的AuthorDTO对应BookEntity中的authorName以及authorBirthday。

    我们只需要添加如下代码即可实现。

    
    mapperFactory.classMap(BookDTO.class, BookEntity.class)
     .field("author.name", "authorName")
     .field("author.birthday", "authorBirthday")
     .byDefault()
     .register();
    
    

    运行结果如下

    
    {"author":{"birthday":-562060800000,"name":"道格拉斯·亚当斯"},"bookName":"银河系漫游指南"}
    
    

    3、枚举类型的属性拷贝

    这时候我们需要新建一个转换器并注册到mapperFactory上。

    注册转换器代码如下

    
    mapperFactory.getConverterFactory().registerConverter("bookTypeConvert", new BidirectionalConverter<BookType, Integer>() {
     @Override
     public Integer convertTo(BookType bookType, Type<Integer> type, MappingContext mappingContext) {
     return bookType.getValue();
        }   @Override
     public BookType convertFrom(Integer value, Type<BookType> type, MappingContext mappingContext) {
     return BookType.getBookType(value);
        } });
    
    

    注册转换器代码如下

    
    mapperFactory.classMap(BookDTO.class, BookEntity.class)
     .field("author.name", "authorName")
     .field("author.birthday", "authorBirthday")
     .fieldMap("bookType", "type").converter("bookTypeConvert").add()
     .byDefault()
     .register();
    
    

    运行结果如下:

    
    {"author":{"birthday":-562060800000,"name":"道格拉斯·亚当斯"},"bookName":"银河系漫游指南","bookType":"NOVEL"}
    
    

    4、JSON类型的属性转换

    该属性的转换原理和上述的枚举类型转换相同,需要创建转换器并注册使用

    
    mapperFactory.getConverterFactory().registerConverter("bookInfoConvert", new BidirectionalConverter<BookInfo, String>() {
     @Override
     public String convertTo(BookInfo bookInfo, Type<String> type, MappingContext mappingContext) {
     return JSON.toJSONString(bookInfo);
        }   @Override
     public BookInfo convertFrom(String s, Type<BookInfo> type, MappingContext mappingContext) {
     return JSON.parseObject(s, BookInfo.class);
        } });
    
    
    
    mapperFactory.classMap(BookDTO.class, BookEntity.class)
     .field("author.name", "authorName")
     .field("author.birthday", "authorBirthday")
     .fieldMap("bookType", "type").converter("bookTypeConvert").add()
     .fieldMap("bookInfo", "bookInformation").converter("bookInfoConvert").add()
     .byDefault()
     .register();
    
    

    运行结果如下

    
    {"author":{"birthday":-562060800000,"name":"道格拉斯·亚当斯"},"bookInfo":{"iSBN":"9787532754687","page":279},"bookName":"银河系漫游指南","bookType":"NOVEL"}
    
    

    是不是很神奇?

    总结

    相信有了这两大神奇Lombok和Orika,基本上实现了和set和get的真正告别。

    项目代码地址:https://github.com/DMinerJackie/rome

    相关文章

      网友评论

      • 望北8261:安卓表示直接写public,不写get set

      本文标题:告别set和get,两大利器轻松搞定model转换

      本文链接:https://www.haomeiwen.com/subject/vhptnftx.html