org.springframework.beans.BeanUtils中copy類屬性值方法copyProperties源碼學習

/**使用的方法爲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方法,否則無法拷貝。首次拷貝沒有緩存,頻繁拷貝則效率較好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章