【org.springframework.beans】public abstract class BeanUtils

更新日期: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 快速开始

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确定给定构造函数所需的参数名称
instantiateClassDeprecated
isSimpleProperty检查给定类型是否表示“简单”属性:简单值类型或简单值类型数组
isSimpleValueType检查给定类型是否表示“简单”值类型:原始或原始包装器、枚举、字符串或其他 CharSequence、数字、日期、时间、URI、URL、区域设置或类。void 和 void 不被视为简单的值类型
resolveSignaturemethodName[([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);
                            }
                        }
                    }
                }
            }
        }

    }
end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值