场景一: 一般我们遇到需要新建 model, 常规做法就是创建一个类, 老老实实的定义好 model 中的所有属性, 一般来说属性对应的 set 方法和 get 方法都是少不了的, 有时候还需要 toString 甚至 equals 和 hashCode 方法.
现在的 IDE 已经很成熟了, 一般不会手写 set 和 get 方法, 采用 IDE 自带的快捷方式自动生成居多. 如下图所示
该方式相对手写方法来说, 效率已经有了很大的提升, 但还是有进一步的提升空间 (下文会介绍). 而且该种方式维护性较差, 当需要修改某个属性名称或者属性类型时, 对应的 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 中找到相应的插件安装并重启即可.
该插件的实现已经放在 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 选择想还原的方法即可
是不是很好用?
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
如果您觉得阅读本文对您有帮助, 请点一下 "推荐" 按钮, 您的 "推荐" 将是我最大的写作动力! 如果您想持续关注我的文章, 请扫描二维码, 关注 JackieZheng 的微信公众号, 我会将我的文章推送给您, 并和您一起分享我日常阅读过的优质文章.
来源: https://www.cnblogs.com/bigdataZJ/p/lombok_and_orika.html