Springframework的BeanUtil.copyProperties()

一、使用

1、引入jar包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.7.RELEASE</version>
</dependency>

2、使用时代码就一行

org.springframework.beans.BeanUtils.copyProperties(source, target,
      ignoreProperties);

源码方法如下:

copyProperties(Object source, Object target)
copyProperties(Object source, Object target, Class<?> editable)
copyProperties(Object source, Object target, String... ignoreProperties)
copyProperties(Object source, Object target, @Nullable Class<?> editable,
			@Nullable String... ignoreProperties)

参数表示要把source里面的属性值拷贝到target对象中,忽略ignoreProperties这些属性。

二、是浅克隆

证明案例如下:

package com.mzy.shejimoshi.PrototypePatternOfSpringBeanUtil;

public class InnerSource {
    private String aa;
    public void setAa(String aa) {
        this.aa = aa;
    }
}
package com.mzy.shejimoshi.PrototypePatternOfSpringBeanUtil;

public class Source {
    private String a;
    private int b;
    private InnerSource s;
    public void setA(String a) {
        this.a = a;
    }
    public void setB(int b) {
        this.b = b;
    }
    public void setS(InnerSource s) {
        this.s = s;
    }
    public String getA() {
        return a;
    }
    public int getB() {
        return b;
    }
    public InnerSource getS() {
        return s;
    }
}
package com.mzy.shejimoshi.PrototypePatternOfSpringBeanUtil;

import org.springframework.beans.BeanUtils;

public class Client {
    public static void main(String[] args) {
        Source m = new Source();
        m.setA("11");
        m.setB(1);
        InnerSource s = new InnerSource();
        s.setAa("22");
        m.setS(s);
        Source target = new Source();
        BeanUtils.copyProperties(m, target);
        System.out.println("m和target的属性a是否相同:" + (target.getA() == m.getA()));
        System.out.println("m和target的属性b是否相同:" + (target.getB() == m.getB()));
        System.out.println("m和target的属性s是否相同:" + (target.getS() == m.getS()));
        System.out.println("m和target是否相同:" + (target == m));
    }
}

执行结果如下:

m和target的属性a是否相同:true
m和target的属性b是否相同:true
m和target的属性s是否相同:true
m和target是否相同:false

三、源码分析

private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
			@Nullable String... ignoreProperties) throws BeansException {

        //判断source和target是否为null,如果是null抛出异常
		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对象的PropertyDescriptor属性数组targetPds
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor targetPd : targetPds) {
		    //获取target对象的targetPd属性的set方法
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
			    //获取source与target属性的同名属性的PropertyDescriptor对象sourcePd
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
				    //获取source与target属性的同名属性的get方法
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
				            //如果get方法不是public方法,就强制反射
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							//获取source属性值
							Object value = readMethod.invoke(source);
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							//把source属性值赋值给target属性
							writeMethod.invoke(target, value);
						}
						catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}

再回到getPropertyDescriptors方法上,这个方法的作用就是内省获取javaBean中的字段,通过获取强引用或者软引用或者新建立一个安全的缓存内省,最后返回的就是javaBean中的有get/set方法的字段。类似于class.getFields

public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
		CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
		return cr.getPropertyDescriptors();
	}
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
		CachedIntrospectionResults results = strongClassCache.get(beanClass);
		if (results != null) {
			return results;
		}
		results = softClassCache.get(beanClass);
		if (results != null) {
			return results;
		}

        //根据class创建CachedIntrospectionResults对象
		results = new CachedIntrospectionResults(beanClass);
		ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse;

		if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
				isClassLoaderAccepted(beanClass.getClassLoader())) {
			classCacheToUse = strongClassCache;
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
			}
			classCacheToUse = softClassCache;
		}

		CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
		return (existing != null ? existing : results);
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值