# transform4j **Repository Path**: itcoon-open/transform4j ## Basic Information - **Project Name**: transform4j - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2021-01-25 - **Last Updated**: 2022-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README TRANSFORM4j ----- ## 一. 简介 Transform4j 是一款轻量、强大、灵活,兼容性强的对象组装转换框架。使用它,程序员可以 用少量的代码轻易的将一个pojo对象转换到另一个pojo对象。拷贝只是它的一个小小的基本功能, 它的强大之处在于快速、可拓展定制、甚至改变对象之间某些结构,让程序员写更少的业务代码。 ## 二. 功能特性 1. 快速、高效。它通过简单的注解让程序在启动时就已经将转换组装的一些定义信息存入缓存,等到 真正转换时直接读取缓存的定义,提高转换的性能和效率 2. 灵活。可以通过注解自定义需要转换的字段、别名。框架还提供了conveter接口可以轻松 定制。另外引入了注入(Inject)机制是更能灵活的组装使用 3. 轻量。框架使用了部分spring boot的功能特性,除了少部分依赖spring boot以外, 不依赖其他的第三方,所以可以很轻松在spring boot工程中集成而不用担心和第三方冲突 4. 简单。通过预先定义的注解,在使用时通过一两行的的代码就可以实现组装转换的功能,轻易上手,简直 不要太轻松 5. 线程安全。转换的过程是线程封闭的 6. 强大。 框架轻松实现多种复杂类型的组装转换:代理对象(包括hibernate代理对象)、匿名对象等, 支持多种类型字段,包括数组、集合、map等 ## 三、名词概念 - 源对象(source):A转换成B,A就是源对象 - 目标对象(target): A转换成B,B就是目标对象 - 转换域(property):源对象和目标对象中要进行转换的某个属性字段 - 编集(assemble): transform的第一个阶段,主要功能是根据目标类型拷贝源对象 - 注入(inject): transform的第二个阶段,该阶段主要对某些转换域进行暴力赋值,可覆盖 编集阶段的的转换域生成的值,注入阶段的优先级更高 - 组装转换(transform): 该框架主要实现的功能,根据目标类型转换源对象,使其“改头换面” ## 四、使用说明 使用上有两种方式,分为注解方式和非注解方式。无论哪种方式,都先maven导入依赖 ~~~ com.itsherman transform-starter 1.0.0.RELEASE ~~~ ### 4.1 注解方式 对于确定的转换目标类型,在程序启动时可以让spring容器扫描,将编集的内容信息加入缓存 ##### 4.1.1 启动类或配置类加上如下注解 ~~~ @EnableTransform4j(groups = { @AssembleGroup(basePackage = "com.itsherman.transform4j.demo.web.vo") }) ~~~ 该段注解告诉了框架,有一组目标类型在`com.itsherman.transform4j.demo.web.vo`包下 ##### 4.1.2 目标对象的定义 目标对象可以由接口定义,也可以由类定义。一般的,我们需要用到两个注解`@AssembleModel`和`@AssembleModelProperty` 如在`com.itsherman.transform4j.demo.web.vo`下,我们定义了两个目标类型 ~~~java @Data @AssembleModel(from = Book.class) public class BookVCO { @AssembleModelProperty private String name; @AssembleModelProperty private Long id; @AssembleModelProperty(mapValue = "author.name") private String authorName; @AssembleModelProperty(mapValue = "author.company.name") private String authorCompanyName; @AssembleModelProperty(mapValue = "author.company.location") private String authorCompanyLocation; } ~~~ ~~~java @AssembleModel(from = Book.class) public interface BookVIO { @AssembleModelProperty Long getId(); @AssembleModelProperty String getName(); @AssembleModelProperty(mapValue = "author.name") String getAuthorName(); @AssembleModelProperty(mapValue = "author.company.name") String getAuthorCompanyName(); @AssembleModelProperty(mapValue = "author.company.location") String getAuthorCompanyLocation(); ~~~ 如上,两个目标类型,一个由类定义,一个由接口定义,两者的源对象类型都是同一个,这两种方式都是可以的。 `@AssembleModelProperty`定义在field或者get方法上,告诉框架这是个转换域(如果字段没有该注解, 则框架不会编集该字段,mapValue 是对应源对象上的属性,支持级联 ##### 4.1.3 编程式使用 ~~~java BookVIO bookVIO = Transformer.to(BookVIO.class).apply(book).done(); ~~~ 上面是最基本的用法,在需要转换的地方(如DO转DTO, DTO转VO,...)加上上面一行的代码 如果使用注入功能,如下: ~~~java List result = Transformer .toList(TeacherVIO.class) .apply(teachers) .setAt("name", name->"变异 " + name) .setAt("student.name",name->"小"+name) .done(); ~~~ 该段代码在编集后对两个属性进行注入,同样支持级联。这个在某些复杂的场景会有用,如某些计算的属性。 利用这个特性,简化我们的代码。 setAt()方法参数有两个,第一个是进行注入的字段,第二个参数是一个Object对象或是一个接口函数(Supplier, Function), Function 是在编集的结果上转换 ### 4.2 非注解方式 该方式不需要任何的注解。这个是考虑到某些场景下,转换的源对象并不是固定的。如通常我们会有很多的请求对象要转换成数据库表的对象, 随着开发,这种请求对象会越来越多,甚至几十个,几百个。这种方式与注解方式的区别是第一次转换会慢一点,并且它会根据目标对象的所有 属性进行编集 ##### 4.2.1 使用 见`4.1.3` ### 4.3 其他 #### 4.3.1 使用转换器(converter) 对于某些特殊的类型,不同的公司的处理方式很可能会不一样(如枚举)。 程序员可以通过实现转换器达到目标。 如下是一个枚举转换器的实现: ~~~java public class IntegerToCodeEnumConverter&CodeEnum> implements AssembleConverter { @Override public boolean support(Type targetType, Object source) { return targetType instanceof Class && ((Class)targetType).isEnum() && CodeEnum.class.isAssignableFrom((Class)targetType) && source.getClass().equals(Integer.class); } @Override public E convert(Type targetType, Integer attribute){ Class codeEnumClass = (Class)targetType; CodeEnum[] codeEnums = codeEnumClass.getEnumConstants(); Optional optional = Arrays.stream(codeEnums).filter(codeEnum -> codeEnum.value().equals(attribute)).findAny(); if(!optional.isPresent()){ throw new AssembleException(String.format("Illegal CodeEnum Value#%s for %s", attribute, targetType.getTypeName())); } return (E)optional.get(); } } ~~~ ## 五、附录 - a)测试demo工程: [transform4j-demo](transform4j-demo) - b)项目源码地址:[transform4j](https://gitee.com/ymx-repository/transform4j.git)