背景:
由于dao层的可视化对象(bean)跟service层以及controller层的bean对象是分开的,也就是同一业务流水线中,在controller层是跟界面或者接口的交互bean,而到了操作数据库层则用的匹配数据库表的实体bean。该俩个bean之间类名可能相同,属性也可能完全一致,就是包路径不一样,为了确保这个过程能够正常对接上,就需要有一个转换的工作名,而这个工作就交给了service层去处理,假若一个bean对象的属性数量相对少的时候,我们可以采取笨方法,挨个getset赋值转换,但假若一个bean对象的属性数量相对庞大,且该项目里存在大量该情况的bean的时候,就需要换个思路,能不能实现自动转换的工具类,因此才有了该工具并并记录于此,希望能够给相同处境的朋友提供上帮助
import org.springframework.util.StringUtils;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import com.test.User;
import com.test.UserB;
public class ConvertUtil {
/**
* 对象转换,前提是俩个对象的属性字段要相同
*
* @param tClass 目标class
* @param object 需要被转换的对象
* @param <T> T
* @return 目标对象
*/
public static <T> T convertObj(Class<T> tClass, Object object) throws IllegalAccessException, InstantiationException {
Class<?> aClass = object.getClass();
Field[] objDeclaredFields = aClass.getDeclaredFields();
AccessibleObject.setAccessible(objDeclaredFields, true);
T tObj = tClass.newInstance();
Arrays.stream(objDeclaredFields).forEach(field -> {
try {
String name = field.getName();
if (name.equals("serialVersionUID")) {
// 现在大部分bean都包含序列号,忽略该值
return;
}
// 该属性对应的值
Object value = field.get(object);
if (Objects.isNull(value) || StringUtils.isEmpty(value.toString().trim()) || isBaseDefaultValue(value)) {
// 无值或空字符串或是基础类型的默认值(如:int型默认值为0,double默认值为0.0,以此类推)
return;
}
// 属性名改成大驼峰
name = name.substring(0, 1).toUpperCase() + name.substring(1);
Method declaredMethod = tClass.getDeclaredMethod("set" + name, new Class[]{value.getClass()});
declaredMethod.invoke(tObj, value);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
});
return tObj;
}
/**
* 判断是否为基本类型的默认值
*
* @param object
* @return
*/
public static boolean isBaseDefaultValue(Object object) {
Class className = object.getClass();
if (className.equals(java.lang.Integer.class)) {
return (int) object == 0;
} else if (className.equals(java.lang.Byte.class)) {
return (byte) object == 0;
} else if (className.equals(java.lang.Long.class)) {
return (long) object == 0L;
} else if (className.equals(java.lang.Double.class)) {
return (double) object == 0.0d;
} else if (className.equals(java.lang.Float.class)) {
return (float) object == 0.0f;
} else if (className.equals(java.lang.Character.class)) {
return (char) object == '\u0000';
} else if (className.equals(java.lang.Short.class)) {
return (short) object == 0;
} else if (className.equals(java.lang.Boolean.class)) {
return (boolean) object == false;
}
return false;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
User user = new User();
user.setId("123456");
user.setName("name");
UserB userB = convertObj(UserB.class, user);
// user的num属性这里没有赋值,但是因为其实基础类型自带默认值, 因此要忽略掉
// UserB(name=name, id=123456, num=null)
System.out.println(userB);
}
}
实体类A 和B (类中的属性名必须保持一致,声明类型如果是基础数据类型,另一个实体类该属性可以是对应的包装类)
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
// 该注解是lombok插件,自动生成getset方法,可以忽略
@Data
public class User implements Serializable {
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private static final long serialVersionUID = 3843044389018289214L;
String name;
// int默认值0
int num;
String id;
}
类名可以不相同,看个人习惯,个人觉得类名区分开还是比较好的,避免后续开发引包引错
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
// 该注解是lombok插件,自动生成getset方法,可以忽略
@Data
public class UserB implements Serializable {
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private static final long serialVersionUID = 3843044389018289214L;
String name;
// int默认值0
int num;
String id;
}
工具类中核心思路是采用反射的getFileds去搜索字段是否有有赋值,之后采用getMethod去获取getset方法,最后利用set方法执行invoke 往实例中注入值,大体处理思路是这样,有兴趣的可以查阅相应反射资料