用途
BeanUtils的copyProperties方法用来做属性copy。
使用时的注意点:- target和source的属性名类型和名称必须相同
- 名称大小写敏感
- 如果名称大小写不一致,或者类型不一致,则跳过,不做属性复制
原理
copyProperties使用了jdk自带的自省机制。 自省 简单来说就是jdk在 反射 上又做了一层包装,针对于Bean对象的属性读写。
大概逻辑如下:
- 遍历target对象的属性
- 获取target对象属性的write方法
- 根据target对象的属性名,获取source对象的属性
- 如果有同样名字的属性,调用source对象属性的read方法得到值
- 调用target对象属性的write方法,给属性赋值
主要源码如下:
public abstract class BeanUtils { //省略其余代码 public static void copyProperties(Object source, Object target) throws BeansException { copyProperties(source, target, null, (String[]) null); } public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException { copyProperties(source, target, editable, (String[]) null); } public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException { copyProperties(source, target, null, ignoreProperties); } private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties ) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } //获取target的属性列表,这里会做class的缓存 PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); //忽略的属性列表 List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null); //循环target的属性列表 for (PropertyDescriptor targetPd : targetPds) { //获取写方法 Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { //根据target的属性名,获取source对应的属性 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { //获取source属性的读方法 Method readMethod = sourcePd.getReadMethod(); //校验写方法的参数类型和读方法的返回类型是否一致,如果不一致则跳过 if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { //如果读方法不是public的,则设置accessible=true if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); //如果写方法不是public的,则设置accessible=true if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } //target.setXXX(source.getXXX()) writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } } } } public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException { CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptors(); } }