一、使用
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);
}