lombok javabean copy异常
lombok javabean copy异常
测试案例
定义两个类:
Test1:
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class Test1 {
private String aaa;
}
Test2:
public class Test2 {
private String aaa;
/**
* @return the aaa
*/
public String getAaa() {
return aaa;
}
/**
* @param aaa the aaa to set
*/
public void setAaa(String aaa) {
this.aaa = aaa;
}
}
写个测试类:
public class Test {
public static void main(String[] args) {
test1();
test2();
test3();
test4();
}
private static void test1() {
System.out.println("test1 复制到 test2");
Test1 t1 = new Test1();
Test2 t2 = new Test2();
t1.setAaa("aaa");
BeanCopier copyLog = BeanCopier.create(Test1.class, Test2.class, false);
copyLog.copy(t1, t2, null);
doPrint(t2.getAaa());
}
private static void test2() {
System.out.println("test2 复制到 test1");
Test1 t1 = new Test1();
Test2 t2 = new Test2();
t2.setAaa("aaa");
BeanCopier copyLog = BeanCopier.create(Test2.class, Test1.class, false);
copyLog.copy(t2, t1, null);
doPrint(t1.getAaa());
}
private static void test3() {
System.out.println("test1 复制到 test1");
Test1 t1 = new Test1();
Test1 t2 = new Test1();
t2.setAaa("aaa");
BeanCopier copyLog = BeanCopier.create(Test1.class, Test1.class, false);
copyLog.copy(t2, t1, null);
doPrint(t1.getAaa());
}
private static void test4() {
System.out.println("test2 复制到 test2");
Test2 t1 = new Test2();
Test2 t2 = new Test2();
t2.setAaa("aaa");
BeanCopier copyLog = BeanCopier.create(Test2.class, Test2.class, false);
copyLog.copy(t2, t1, null);
doPrint(t1.getAaa());
}
private static void doPrint(String src) {
if (StringUtil.isEmpty(src)) {
System.out.println("复制失败,属性为空");
}
else {
System.out.println("复制成功,值为:" + src);
}
}
}
运行结果:
test1 复制到 test2
复制成功,值为:aaa
test2 复制到 test1
复制失败,属性为空
test1 复制到 test1
复制失败,属性为空
test2 复制到 test2
复制成功,值为:aaa
分析
使用lombok生成的set方法返回不是void类型
跟踪BeanCopier初始化过程:
BeanCopier
public static BeanCopier create(Class source, Class target, boolean useConverter)
{
Generator gen = new Generator(); // 这里进去
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
Generator
public void generateClass($ClassVisitor v)
{
$Type sourceType = $Type.getType(source);
$Type targetType = $Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(46, 1, getClassName(), BeanCopier.BEAN_COPIER, null, "<generated>");
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(1, BeanCopier.COPY, null);
PropertyDescriptor getters[] = ReflectUtils.getBeanGetters(source);
PropertyDescriptor setters[] = ReflectUtils.getBeanSetters(target);
Map names = new HashMap();
for(int i = 0; i < getters.length; i++)
names.put(getters[i].getName(), getters[i]);
net.sf.cglib.core.Local targetLocal = e.make_local();
net.sf.cglib.core.Local sourceLocal = e.make_local();
if(useConverter)
{
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(int i = 0; i < setters.length; i++)
{
PropertyDescriptor setter = setters[i];
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
if(getter == null)
continue;
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod()); // 这里是找目标对象的set方法
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(BeanCopier.CONVERTER, BeanCopier.CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
continue;
}
if(compatible(getter, setter))
{
e.dup2();
e.invoke(read);
e.invoke(write);
}
}
e.return_value();
e.end_method();
ce.end_class();
}
PropertyDescriptor
/**
* Gets the method that should be used to write the property value.
*
* @return The method that should be used to write the property value.
* May return null if the property can't be written.
*/
public synchronized Method getWriteMethod() {
Method writeMethod = this.writeMethodRef.get();
if (writeMethod == null) {
Class<?> cls = getClass0();
if (cls == null || (writeMethodName == null && !this.writeMethodRef.isSet())) {
// The write method was explicitly set to null.
return null;
}
// We need the type to fetch the correct method.
Class<?> type = getPropertyType0();
if (type == null) {
try {
// Can't use getPropertyType since it will lead to recursive loop.
type = findPropertyType(getReadMethod(), null);
setPropertyType(type);
} catch (IntrospectionException ex) {
// Without the correct property type we can't be guaranteed
// to find the correct method.
return null;
}
}
if (writeMethodName == null) {
writeMethodName = Introspector.SET_PREFIX + getBaseName();
}
Class<?>[] args = (type == null) ? null : new Class<?>[] { type };
writeMethod = Introspector.findMethod(cls, writeMethodName, 1, args);
if (writeMethod != null) {
if (!writeMethod.getReturnType().equals(void.class)) { // 这里是关键点
writeMethod = null;
}
}
try {
setWriteMethod(writeMethod);
} catch (IntrospectionException ex) {
// fall through
}
}
return writeMethod;
}
解决方法
-
Test1改为
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = false) public class Test1 { private String aaa; } -
使用原始的get/set方法
结论
lombok使用的时候一定要注意参数的定义,风险较高

浙公网安备 33010602011771号