更新日期:2022/05/13
ɪz ɪt ðə dɪˈstɔːʃ(ə)n əv hjuːˈmænəti ɔːr ðə kəˈlæps əv məˈræləti ðæt laɪz bɪˈhaɪnd ɔːl ðɪs ?
- 1. BeanUtils 快速开始
- 2. BeanUtils 方法总览
- 3. BeanUtils 使用示例
- 4. BeanUtils.copyProperties 浅拷贝
- 4.1 public static void copyProperties(Object source, Object target) throws BeansException
- 4.2 copyProperties(Object source, Object target, Class> editable)
- 4.3 copyProperties(Object source, Object target, String... ignoreProperties)
- 5. BeanUtils.copyProperties 源码
1. BeanUtils 快速开始
BeanUtils JavaBeans 的静态便捷方法。利用反射机制对 JavaBean 的属性进行处理,用于实例化 bean、检查 bean 属性类型、复制 bean 属性等。
- 通常的 bean 属性复制
SourceBean source = new SourceBean("stringParam1", 2, stringList);
TargetBean target = new TargetBean();
target.setStringParam1(source.getStringParam1());
target.setIntegerParam2(source.getIntegerParam2());
target.setStringListParam3(source.getStringListParam3());
System.out.println(target);
- BeanUtils 类的 bean 属性复制
SourceBean source = new SourceBean("stringParam1", 2, stringList);
TargetBean target = new TargetBean();
BeanUtils.copyProperties(source, target);
2. BeanUtils 方法总览
- BeanUtils.class
方法 | 说明 |
---|---|
copyProperties | 将给定源 bean 的属性值复制到目标 bean |
findDeclaredMethod | 查找一个具有给定方法名和给定参数类型的方法,该方法在给定的类或其超类中声明 |
findDeclaredMethodWithMinimalParameters | 查找一个具有给定的方法名称和最小参数(最好的情况是:没有)的方法,并在给定的类或其超类中声明 |
findMethod | 查找一个具有给定方法名和给定参数类型的方法,该方法在给定的类或其超类中声明 |
findMethodWithMinimalParameters | 查找一个具有给定的方法名称和最小参数(最好的情况是:没有)的方法,并在给定的类或其超类中声明 |
findEditorByConvention | 查找遵循“编辑器”后缀约定的 JavaBeans PropertyEditor |
findPrimaryConstructor | 返回提供的类的主构造函数。对于 Kotlin 类,这将返回与 Kotlin 主构造函数对应的 Java 构造函数(如 Kotlin 规范中所定义)。否则,特别是对于非 Kotlin 类,这只会返回 null |
findPropertyForMethod | 查找给定方法的 JavaBeans PropertyDescriptor,该方法可以是该 bean 属性的读取方法或写入方法 |
findPropertyType | 如果可能,从给定的类/接口确定给定属性的 bean 属性类型 |
getResolvableConstructor | 为所提供的类返回一个可解析的构造函数,可以是一个主要的或带有参数的单一公共构造函数,或带有参数的单一非公共构造函数,或只是一个默认构造函数 |
getPropertyDescriptor | 检索给定属性的 JavaBeans PropertyDescriptor |
getPropertyDescriptors | 检索给定属性的 JavaBeans PropertyDescriptors |
getWriteMethodParameter | 为指定属性的 write 方法获取一个新的 MethodParameter 对象 |
getParameterNames | 确定给定构造函数所需的参数名称 |
instantiateClass | |
isSimpleProperty | 检查给定类型是否表示“简单”属性:简单值类型或简单值类型数组 |
isSimpleValueType | 检查给定类型是否表示“简单”值类型:原始或原始包装器、枚举、字符串或其他 CharSequence、数字、日期、时间、URI、URL、区域设置或类。void 和 void 不被视为简单的值类型 |
resolveSignature | 以 methodName[([arg_list])] 形式解析方法签名,其中 arg_list 是可选的、以逗号分隔的完全限定类型名称列表,并尝试根据提供的类解析该签名 |
3. BeanUtils 使用示例
*************************************************************************
public class BeaUtilsTest {
public static void main(String[] args) {
Method m1 = BeanUtils.findMethod(SubClass.class, "methodSup", String.class);
Method m2 = BeanUtils.findMethodWithMinimalParameters(SuperClass.class, "methodSup");
Method m3 = BeanUtils.resolveSignature("methodSup", SubClass.class);
System.out.println("findMethod: " + m1);
System.out.println("findMethodWithMinimalParameters: " + m2);
System.out.println("resolveSignature: " + m3);
System.out.println("**************************************");
PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(SubClass.class);
for (PropertyDescriptor pd : propertyDescriptors) {
System.out.println(pd.getReadMethod());
}
}
}
【Console】--------------------------------------------------------------
findMethod: public void com.ouseki.itgodroad.ToBeSynchronized.BeaUtilsTest$SuperClass.methodSup(java.lang.String)
findMethodWithMinimalParameters: public void com.ouseki.itgodroad.ToBeSynchronized.BeaUtilsTest$SuperClass.methodSup()
resolveSignature: public void com.ouseki.itgodroad.ToBeSynchronized.BeaUtilsTest$SuperClass.methodSup()
**************************************
public final native java.lang.Class java.lang.Object.getClass()
public java.lang.String com.ouseki.itgodroad.ToBeSynchronized.BeaUtilsTest$SubClass.getP11()
public java.lang.Integer com.ouseki.itgodroad.ToBeSynchronized.BeaUtilsTest$SubClass.getP12()
*************************************************************************
4. BeanUtils.copyProperties 浅拷贝
在 Java 系统工程开发过程中,都会有各个层之间的对象转换,比如 VO、DTO、PO 等,如果都是手动 get、set 太浪费时间而且还可能 *** 作错误,所以选择一个自动化工具会更加方便。
4.1 public static void copyProperties(Object source, Object target) throws BeansException- public static void copyProperties(Object source, Object target) throws BeansException
注意:源类和目标类不必匹配,甚至不必相互派生,只要属性匹配即可。
源类的 integerParam2 没有成功拷贝到目标类的 stringParam2 ,赋值 null 。
内部类虽然一模一样,但是并没有被拷贝,因为他们分别属于各自的内部类:SourceBean.InnerClass、TargetBean.InnerClass;
篇幅过长,只展示属性。
*************************************************************************
public class BeaUtilsTest {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add(null);
stringList.add("2");
SourceBean source = new SourceBean("stringParam1", 2, stringList, new SourceBean.InnerClass("p1", 2));
TargetBean target = new TargetBean();
BeanUtils.copyProperties(source, target);
System.out.println(source);
System.out.println(target);
}
}
.........................................................................
class SourceBean {
private String stringParam1;
protected Integer integerParam2;
public List<String> stringListParam3;
public SourceBean.InnerClass innerClass;
static class InnerClass {
private String p1;
protected Integer p2;
}
.........................................................................
class TargetBean {
private String stringParam1;
protected String stringParam2;
public List<String> stringListParam3;
public TargetBean.InnerClass innerClass;
static class InnerClass {
private String p1;
protected Integer p2;
}
}
【Console】--------------------------------------------------------------
SourceBean{stringParam1='stringParam1', integerParam2=2, stringListParam3=[null, 2], innerClass=innerClass{p1='p1', p2=2}}
TargetBean{stringParam1='stringParam1', stringParam2='null', stringListParam3=[null, 2], innerClass=null}
*************************************************************************
4.2 copyProperties(Object source, Object target, Class> editable)
- public static void copyProperties(Object source, Object target, Class> editable) throws BeansException
将给定源 bean 的属性值复制到给定目标 bean 中,仅设置给定“可编辑”类(或接口)中定义的属性。
目标类应继承或实现“可编辑”类(或接口)。
篇幅过长,只展示属性。
*************************************************************************
public class BeaUtilsTest {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add(null);
stringList.add("2");
SourceBean source = new SourceBean("stringParam1", 2, stringList, new SourceBean.InnerClass("p1", 2));
TargetBean target = new TargetBean();
BeanUtils.copyProperties(source, target);
System.out.println(source);
System.out.println(target);
}
}
.........................................................................
class SourceBean {
private String stringParam1;
protected Integer integerParam2;
public List<String> stringListParam3;
public SourceBean.InnerClass innerClass;
static class InnerClass {
private String p1;
protected Integer p2;
}
}
.........................................................................
class TargetBean extends EditableBean{
private String stringParam1;
protected String stringParam2;
public List<String> stringListParam3;
public SourceBean.InnerClass innerClass;
static class InnerClass {
private String p1;
protected Integer p2;
}
}
.........................................................................
class EditableBean {
private String stringParam1;
protected Integer integerParam2;
}
【Console】--------------------------------------------------------------
SourceBean{stringParam1='stringParam1', integerParam2=2, stringListParam3=[null, 2]}
TargetBean{stringParam1='stringParam1', stringParam2='null', stringListParam3=null}
*************************************************************************
4.3 copyProperties(Object source, Object target, String… ignoreProperties)
- public static void copyProperties(Object source, Object target, String… ignoreProperties) throws BeansException
将给定源 bean 的属性值复制到给定目标 bean 中,忽略给定的“ignoreProperties”。
*************************************************************************
public class BeaUtilsTest {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add(null);
stringList.add("2");
SourceBean source = new SourceBean("stringParam1", 2, stringList, new SourceBean.InnerClass("p1", 2));
TargetBean target = new TargetBean();
String[] ignoreFields = {"stringListParam3"};
BeanUtils.copyProperties(source, target, ignoreFields);
System.out.println(source);
System.out.println(target);
}
}
【Console】--------------------------------------------------------------
SourceBean{stringParam1='stringParam1', integerParam2=2, stringListParam3=[null, 2], innerClass=innerClass{p1='p1', p2=2}}
TargetBean{stringParam1='stringParam1', stringParam2='null', stringListParam3=null, innerClass=null}
*************************************************************************
5. BeanUtils.copyProperties 源码
已添加注释说明。从源码中可以得出以下结论:
1.需要对应的属性有 getter 和 setter 方法;
2.目标类的属性必须是源类的实例(Integer 可以转化为 String);
3.如果存在属性完全相同的内部类,也不算是同一个内部类,即分别属于各自的内部类,spring 会认为属性不同,不会 copy;
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
// 源类和目标类都不能为 null
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
// 暂定转换类为目标类
Class<?> actualEditable = target.getClass();
// 如果可编辑类不为 null 继续判断该类是否为目标类的实例,是的话则目标类改为可编辑类
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
// 获取目标类的属性描述符
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
// 判断是否忽略给定的属性
List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
PropertyDescriptor[] var7 = targetPds;
int var8 = targetPds.length;
// 遍历目标类的
for(int var9 = 0; var9 < var8; ++var9) {
// 获取属性 *** 作符
PropertyDescriptor targetPd = var7[var9];
// 获取应该用于写入属性值的方法(set方法)
Method writeMethod = targetPd.getWriteMethod();
// 如果具有写入方法,且忽略属性为空或不存在忽略列表中,则进行 copy *** 作
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
// 在源类中查找目标类中的 *** 作符
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
// 如果源类中找到了同名属性
if (sourcePd != null) {
// 获取该属性的 get 方法
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
// 获取源类中该属性 get 方法的返回值类型
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
// 获取目标类中该属性 set 方法的参数类型
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
// 如果源类和目标类都不具有无法解析的通用性,则继续判断目标类是否为源类的实例
boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
// 如果可以互相转化
if (isAssignable) {
try {
// 如果 get 方法的修饰符为非public,则暴力破解
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
// 读取源类中的值
Object value = readMethod.invoke(source);
// 如果 set 方法的修饰符为非public,则暴力破解
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 将源类的值写入到目标类中
writeMethod.invoke(target, value);
} catch (Throwable var18) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
}
}
}
}
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)