/**使用的方法为copyProperties(Object source, Object target)**/
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, (Class)null, (String[])null);
}
上面方法调用copyProperties(Object source, Object target, @Nullable Class> editable, @Nullable String… 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");
/**
//对象为空则抛出异常IllegalArgumentException
public static void notNull(@Nullable Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
**/
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;
}
//获取PropertyDescriptor(属性描述器)数组,getPropertyDescriptors具体内容看下方
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];
//getWriteMethod中注释说明
//May return null if the property can't be written.
//也就是说对应的类中必须有set(写入)方法,否则返回空
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
//取出对应的属性并读取值
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
//写入对应的值到目标类
writeMethod.invoke(target, value);
} catch (Throwable var15) {
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
}
}
}
}
}
}
getPropertyDescriptors(actualEditable);方法分析
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
//CachedIntrospectionResults是Spring中的一个用于缓存javaBeans的PropertyDescriptor的类
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
//返回PropertyDescriptor数组
return cr.getPropertyDescriptors();
}
//整体分析发现,这是一个从使用缓存存取类的方法
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
/**
* strongClassCache是一个ConcurrentMap,即线程安全的集合
* static final ConcurrentMap, CachedIntrospectionResults> strongClassCache = new ConcurrentHashMap(64);
* 如果获取不到,则会继续从softClassCache中进行获取,类型同样也是ConcurrentMap
*/
CachedIntrospectionResults results = (CachedIntrospectionResults)strongClassCache.get(beanClass);
if (results != null) {
return results;
} else {
//static final ConcurrentMap, CachedIntrospectionResults> softClassCache = new ConcurrentReferenceHashMap(64);
//ConcurrentReferenceHashMap是自spring3.2后增加的一个同步的可以指定引用级别的Map
//这里new ConcurrentReferenceHashMap(64);使用的是默认的引用级别-SOFT(软引用)
results = (CachedIntrospectionResults)softClassCache.get(beanClass);
if (results != null) {
return results;
} else {
//创建CachedIntrospectionResults
results = new CachedIntrospectionResults(beanClass);
ConcurrentMap classCacheToUse;
//根据方法名理解,判断缓存是否安全
if (!ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) && !isClassLoaderAccepted(beanClass.getClassLoader())) {
//缓存不安全,则不使用强引用
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
classCacheToUse = softClassCache;
} else {
//使用强引用
classCacheToUse = strongClassCache;
}
//存入classCacheToUse并返回(存在则直接返回)
CachedIntrospectionResults existing = (CachedIntrospectionResults)classCacheToUse.putIfAbsent(beanClass, results);
return existing != null ? existing : results;
}
}
}
总结,copyProperties通过反射以及缓存,对类的属性值进行copy,目标类必须需要拷贝的属性必须拥有对应的set方法,否则无法拷贝。首次拷贝没有缓存,频繁拷贝则效率较好。