Spring BeanCopier拷贝指定字段,过滤空数据字段
大约 3 分钟
提示
Spring BeanCopier经过大量测试, 通过生成字节码class的方式, 直接拷贝值, 性能可以比肩get/set方法
但是在实际开发时, 遇到bean部分字段拷贝, 过滤null值拷贝需求
1. 重写BeanCopyier方法
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.map.LRUMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import javax.validation.constraints.NotNull;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Error
*/
public abstract class MyBeanCopier {
// 生成的BeanCopier唯一值
private static final BeanCopierKey KEY_FACTORY = (BeanCopierKey) KeyFactory.create(BeanCopierKey.class);
// converter 转换接口, converter接口 Object convert(Object var1, Class var2, Object var3) var1 传入值, var2 传入值class, var3 传入源方法名称
private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
// 定义当前类, 注意修改类路径!!!
private static final Type BEAN_COPIER = TypeUtils.parseType("com.zxcsoft.MyBeanCopier");
// 定义生成class的 copy 方法
private static final Signature COPY = new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER});
// 定义convert接口的conver方法
private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)");
// 定义Objects.nonNull()方法, 在过滤对象属性时使用本方法过滤
private static final MethodInfo NON_NULL = ReflectUtils.getMethodInfo(ReflectUtils.findMethod("java.util.Objects.nonNull(java.lang.Object)"));
// LRU缓存生成的MyBeanCopier方法
private static final Map<BeanCopierKey, MyBeanCopier> COPIER_CACHE = Collections.synchronizedMap(new LRUMap<>(2000, 100));
// 根据参数生成, 缓存MyBeanCopier
public static MyBeanCopier create(Class<?> source, Class<?> target, boolean skipNull, boolean useConverter, List<String> fields) {
BeanCopierKey key = (BeanCopierKey) KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter, StringUtils.join(fields, ","));
return COPIER_CACHE.computeIfAbsent(key, k -> {
Generator gen = new Generator();
gen.setSkipNull(skipNull);
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
gen.setFields(fields);
return gen.create(key);
});
}
/**
* 拷贝树形
*
* @param from 从对象
* @param to 目标对象
* @param converter 转换器
*/
public abstract void copy(Object from, Object to, Converter converter);
/**
* key对象
*/
interface BeanCopierKey {
Object newInstance(String source, String target, boolean useConverter, String fields);
}
/**
* 转换
*/
public static class Generator extends AbstractClassGenerator<Object> {
private static final Source SOURCE = new Source(MyBeanCopier.class.getName());
private Class<?> source;
private Class<?> target;
private boolean useConverter;
/**
* 添加的需要转换的字段
*/
private List<String> fields;
/**
* 是否需要跳过数据为null的字段
*/
private boolean skipNull;
public Generator() {
super(SOURCE);
}
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
}
public void setSource(Class<?> source) {
if (!Modifier.isPublic(source.getModifiers())) {
setNamePrefix(source.getName());
}
this.source = source;
}
public void setTarget(Class<?> target) {
if (!Modifier.isPublic(target.getModifiers())) {
setNamePrefix(target.getName());
}
this.target = target;
}
public void setUseConverter(boolean useConverter) {
this.useConverter = useConverter;
}
public void setFields(List<String> fields) {
this.fields = fields;
}
public void setSkipNull(boolean skipNull) {
this.skipNull = skipNull;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return source.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(source);
}
public LattoBeanCopier create(BeanCopierKey key) {
return (LattoBeanCopier) super.create(key);
}
@Override
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
Map<String, PropertyDescriptor> names = new HashMap<>(16);
for (PropertyDescriptor propertyDescriptor : getters) {
names.put(propertyDescriptor.getName(), propertyDescriptor);
}
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
if (useConverter || skipNull) {
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
} else {
e.load_arg(1);
e.checkcast(targetType);
e.load_arg(0);
e.checkcast(sourceType);
}
for (PropertyDescriptor setter : setters) {
// 这里过滤字段
if (CollectionUtils.isNotEmpty(fields) && !fields.contains(setter.getName())) {
continue;
}
PropertyDescriptor getter = names.get(setter.getName());
if (getter != null) {
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
// 使用convert时, convert自己实现跳过null
if (useConverter) {
Type setterType = write.getSignature().getArgumentTypes()[0];
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(sourceLocal);
e.invoke(read);
e.box(read.getSignature().getReturnType());
EmitUtils.load_class(e, setterType);
e.push(write.getSignature().getName());
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
} else if (compatible(getter, setter)) {
// 是否需要跳过null
if (skipNull) {
// 添加结束标记
Label end = e.make_label();
// 加载source
e.load_local(sourceLocal);
// 执行读取,结果压入堆栈
e.invoke(read);
// 堆栈数据执行Objects.nonNull(), 结果压入堆栈
e.invoke(NON_NULL);
// 判断是否为true, false, false则跳转到end标记
e.if_jump(Constants.IFEQ, end);
// 加载target
e.load_local(targetLocal);
// 再次加载source
e.load_local(sourceLocal);
// 读取数据压入堆栈
e.invoke(read);
// 读取堆栈数据
e.invoke(write);
// 标记当前为结束
e.mark(end);
} else {
e.dup2();
e.invoke(read);
e.invoke(write);
}
}
}
}
e.return_value();
e.end_method();
ce.end_class();
}
@Override
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type);
}
@Override
protected Object nextInstance(Object instance) {
return instance;
}
}
}
2. 测试
@Data
class A {
private String name;
private Integer age;
}
@Data
class B {
private String name;
private Integer age;
private String six;
}
void main(){
A a = new A();
a.setName("name");
a.setAge(18);
B b = new B();
MyBeanCopier copier = MyBeanCopier.create(A.class, B.class, true, false, null);
copier.copy(a, b, null);
}