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, 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方法,否则无法拷贝。首次拷贝没有缓存,频繁拷贝则效率较好。

你可能感兴趣的:(java)