Java属性映射工具moon-mapper

介绍

最近换工作,趁这期间写了这个java属性映射工具,前后用了大概两个整周吧。

写的时候参考了现在已有的一些映射工具如:mapstructDozerOrikaBeanUtilsPropertyUtilsBeanCopier等,从使用性能、功能、方便等角度写的这个属性映射工具,下面隆重推出今天的主角:moon-mapper

接下来从使用和原理两个角度介绍一下这个工具,已经发布到maven仓库欢迎各种暴力测试,也希望大家使用过后多多提意见,让它更加强大。

一、使用介绍

Github: github.com/moonsky-all…

Gitee: gitee.com/moonsky-all…

1. 安装
<dependency>
  <groupId>io.github.moonsky-all</groupId>
  <artifactId>moon-mapper</artifactId>
  <version>0.1</version>
</dependency>
复制代码
2. 基本使用

2.1. 注册映射器只需要在其中一个类上添加注解MapperFor指定要映射的类即可定义两个类之间的映射关系;

2.2. 获取映射器BeanMapper mapper = Mapper2.get(Class, Class)

2.3. 使用映射器mapper.doForward(obj1, obj2)

public class UserEntity {

    private String username;
    
    private LocalDateTime birthday;
    
    // 省略其他字段、getter、setter
}

// 添加这个注解就可以定义两个类之间具有映射关系
// 这里是 Class 数组,可以定义多个映射关系
@MapperFor({UserEntity.class})
public class UserDO {

    // 获取映射器
    // thisPrimary 是个简便方法,只能在有 MapperFor 注解的类使用
    // 它获取的是本类与 MapperFor 中定义的第一个类之间的映射器
    private static final BeanMapper MAPPER = Mapper2.thisPrimary();

    private String username;
    
    // 这个注解是用作格式化的,可用于格式化(解析)日期和数字
    // 在非日期/数字与字符串之间的关系上会忽略掉
    @MappingFormat("yyyy-MM-dd HH:mm:ss")
    private String birthday;
    
    // 省略其他字段、getter、setter
}

public class Main {
    
    // Mapper2.get(Class, Class) 这是普通获取映射器的方式
    private static final BeanMapper MAPPER = Mapper2.get(UserDO.class, UserEntity.class);
    
    public static void main(String[] args) {
        UserDO userDo = new UserDO();
        
        // mapper 还有其他方法,可正向、也可反向映射
        UserEntity entity = MAPPER.doForward(userDo, new UserEntity());
    }
}
复制代码
3. 自动隐式类型转换

Mapper支持常用数据类型之间的自动类型转换:

  • 基本数据类型: byteshortintlongfloatdoublecharboolean;
  • 基本数据类型包装类: ByteShortIntegerLongFloatDoubleCharacterBoolean;
  • BigDecimalBigInteger;
  • 日期类型: java.util.Dateutil.Calendarsql.Datesql.Timesql.Timestampjava.time.XxxXxxjoda.time.XxxXxx等【建议joda-time 2.x+】;
  • 枚举与字符串、数字之间的转换;
  • 日期/数字 与字符串之间的格式化和解析字符串;
4. 基于setter重载的自定义转换器

上面提到了默认的数据类型自动转换功能,但是有些情况下肯定是不能满足使用场景的, 所以另外提供了基于setter重载的自定义转换器,由于java语言特性,同名不同参数类型的方法可以重载, 当为字段提供另一个不同数据类型的setter方法时,这里就相当于提供了一个指定类型的数据转换器,如:

public enum GenderEnum {
    MALE("男"),
    FEMALE("女");
    
    public final String text;
    
    GenderEnum(String text) { this.text = text; }
}

public class MemberEntity {

    private GenderEnum gender;
    
    // 省略其他字段、getter、setter
    
    // 增加一个 String 类型的 gender setter 方法就是自定义转换器
    public void setGender(String text) {
        if ("男".equals(text)) {
            this.gender = GenderEnum.MALE;
        } else if ("女".equals(text)) {
            this.gender = GenderEnum.FEMALE;
        }
    }
}
复制代码

可见自定义转换器还是比较简便的,而且充分利用了IDE的代码提示功能以及java语言特性,尽量减小用字符串的方式写代码也是当初设计moon-mapper的一个初衷(会不会有人看到这句话感觉奇怪,为什么用字符串写代码?这个稍后会讲)

5. 支持Spring环境

如果是在Spring环境下(做Java基本都在这环境吧[狗头]),如果应用的数据类是在Spring扫描范围内可以使用自动注入,如:

@Service
public class BusinessService {

    // Spring 自动注入
    @Autowired
    private BeanMapper<UserDO, UserEntity> userMapper;
}
复制代码

整体使用下来还是比较简单,添加注解MapperFor注册,然后用Mapper2获取映射器就可以使用了,接下来说明一下实现原理。

二、实现原理

Mapper2是基于静态编译生成的映射器,这一点mapstruct类似,生成的代码和手写get/set一样,所以性能上是有保证的。

@MapperFor注册映射器后会生成两个Copier分别代表正向和反向的属性复制器,和一个Mapper映射器对前两个Copier做了简单封装,在Spring环境下都会添加注解@Component,这样就可以实现自动注入了。

提示:
每次需要重新编译时记得Build -> Rebuild Project(在IDEA中);
另外mvn clean compile也会执行这一过程

属性复制是基于gettersetter方法进行的,所以参与复制的属性必须有public修饰的setter/getter方法,同时在处理映射关系时,会静态分析每个属性的数据类型,相同类型的同名属性可直接双向映射。如果是不同数据类型,优先使用基于setter重载的自定义转换器,然后分析是否可使用一些默认转换,比如数字之间的转换、Datelong之间的转换、日期/数字格式化等,如果能转换就按转换的规则映射,否则就忽略同名不同类型和不同名属性的映射。

三、说明

  1. 支持joda-time日期库,但最好使用2.x以上的版本,2.x以下的版本虽然也做了一部分针对性处理,但不完善;
  2. 目前总体分成了mapperprocessing两个包,其中processing包里的所有内容最好都不要用,将来这个包一定会剥离出去;
  3. 不同名属性之间的映射,目前没有单独提供这一功能,但可通过添加同名setter达到相同效果,目前这是第一版,以后会改进的;
  4. 目前是第一版,仅实现了最基础的功能,希望大家能多提提意见和改进方案!!!
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享