/**使用的方法爲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<Class<?>, CachedIntrospectionResults> strongClassCache = new ConcurrentHashMap(64);
* 如果獲取不到,則會繼續從softClassCache中進行獲取,類型同樣也是ConcurrentMap
*/
CachedIntrospectionResults results = (CachedIntrospectionResults)strongClassCache.get(beanClass);
if (results != null) {
return results;
} else {
//static final ConcurrentMap<Class<?>, 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方法,否則無法拷貝。首次拷貝沒有緩存,頻繁拷貝則效率較好。