当前位置: 首页 > >

一个简单易用的 Javabean 拷贝工具

发布时间:

Javabean 拷贝工具有不少,常见的有 Apache Commons 和 Spring 的 BeanUtils,以及 CGlib 性能强劲的 BeanCopier。它们可能足够好,尤其是 BeanCopier,甚至可以提供媲美直接调用 getters/setters 的性能…


但是…它们提供的功能要么不够用,要么不易用。于是我从自身需求出发,完成了下面这个 Javabean 拷贝工具,名字姑且就叫做 BeanCopyUtils。由于对我来说,性能的优先级暂时还不是很高,所以它可能并不适合看重性能的使用者。


GitHub 地址在这里,欢迎大家体验使用,如果发现 Bug 或是有什么好的改进想法,欢迎提 PR。


以下是 README 的中文版本。


特性
深拷贝.支持按条件拷贝指定的字段。支持不同名或不同类型字段间的拷贝。支持拷贝 Bean 的数组或者集合(Collection)。
使用
基础

例如有两个类,一个作为源,另一个作为目标。


public class Source {
private long id = 1;
private int[] numbers = { 1, 2, 3 };
public String getName() {
return "source";
}
}

public class Target {
private long id;
private int[] numbers;
private String name;
}

想要拷贝一个源对象到目标对象,只需要使用:


Target target = BeanCopyUtils.copy(new Source(), Target.class);

或是,


Target target = new Target();
BeanCopyUtils.copy(new Source(), target);

如你所见,getters/setters 并不是必需的,然而,一个合法的 Javabean 应该具备它们。


高级
字段重映射

默认情况下,两个 Javabean 字段间是通过字段名来进行对应的。对于那些名称不同的字段,可以使用 @AliasFor 注解来指明被注解的字段是另一个 Javabean 中某个字段的别名,如下所示:


public class Source {
private long id;
}

public class Target {
@AliasFor("id")
private long userId;
}

转换器

通过 @Converter注解来提供一个自定义的转换器类,自定义的转换器类必须实现 Converter 接口。例如:


public class TimestampToDateConverter implements Converter {
@Override
public Date convert(Long timestamp) {
return new Date(timestamp);
}
}

public class Source {
private long timestamp;
}

public class Target {
@AliasFor("timestamp")
@Converter(TimestampToDateConverter.class)
private Date date;
}

条件拷贝

通过 @CopyIgnore 来指明当(或除了)某种条件满足时,被注解的字段应该在拷贝时被忽略。此外,你也可以指定是否要忽略值为 null 或空的字段。



空字符串、数组、集合或 0 都被认为是一个空值,包括 null



来看看下面的类:


public class Bean {
public static final String COPY_WITH_ID = "copyWithId";
public static final String COPY_WITHOUT_NAME = "copyWithoutName";

@CopyIgnore(except = COPY_WITH_ID)
private int id;
@CopyIgnore(when = COPY_WITHOUT_NAME, policy = IgnorePolicy.EMPTY)
private String name;
}

很明显,不难去理解上面这些注解做了些什么。字段 id 总会在拷贝时被忽略,除非当你想要 COPY_WITH_ID, 而字段 name 只有当你想要 COPY_WITHOUT_NAME 或者它的值为空的时候才会被忽略。


两个字符串常量用来作为条件,可在拷贝的时候以数组的形式指定。例如:


// `id` 和 `name` 都会被拷贝
BeanCopyUtils.copy(new Bean(), Bean.class, new String[] { Bean.COPY_WITH_ID });
// `id` 和 `name` 都会被忽略
BeanCopyUtils.copy(new Bean(), Bean.class, new String[] { Bean.COPY_WITHOUT_NAME });


注意,由于条件都是简单的字符串常量,所以你可能需要保证它们的唯一性。



你也可以决定是否要忽略所有值为 null 或空的字段:


BeanCopyUtils.copy(new Bean(), Bean.class, IgnorePolicy.EMPTY);

然而,注解上的配置有更高的优先级,因此你可以针对某个字段进行一些额外配置。


集合

一般来说,对于 Collection 类型的目标字段,并不需要额外的配置。


如果目标字段的类型是接口,比如 List,那么被拷贝的值会和源字段类型相同。如果这不是你想要的,你可以直接将目标字段声明为一个实现类, 比如 ArrayList。鉴于使用接口类是一个好的做法,你可以通过 @ToCollection 来指定一个实现类。


public class Bean {
@ToCollection(ArrayList.class)
private List numbers;
private ArrayList names;
}

性能

性能其实并没有怎么测试,但应该能接受。


希望您能对此提供一些建议,甚至是亲自帮助改进!欢迎 PR。



友情链接: