在咱們着手一個Java Web項目的時候,常常會遇到DO、VO、DTO
對象之間的屬性拷貝,若採用get、set
的方法來進行賦值的話,代碼會至關冗長醜陋,通常咱們會採用Spring
的BeanUtils
類來進行屬性拷貝,其基本原理就是經過Java的反射機制,下面咱們來看一下源碼的具體實現。java
在分析源碼前,咱們先溫習一下如下的知識點。segmentfault
在Java中萬物皆對象,並且咱們在代碼中寫的每個類也都是對象,是java.lang.Class
類的對象。因此,每一個類都有本身的實例對象,並且它們本身也都是Class
類的對象。api
咱們來看一下Class
類的構造方法:緩存
private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
Class類的構造方法是私有的,只有JVM能夠建立該類的對象,所以咱們沒法在代碼中經過new
的方式顯示聲明一個Class對象。安全
可是,咱們依然有其餘方式得到Class類的對象:app
1.經過類的靜態成員變量ide
Class clazz = Test.class;
2.經過對象的getClass()方法工具
Class clazz = test.getClass();
3.經過Class的靜態方法forName()源碼分析
// forName須要傳入類的全路徑 Class clazz = Class.forName("destiny.iron.api.model.Test");
基本類型和其對應的包裝類的Class對象是不相等的,即long.class != Long.class
。性能
PropertyDescriptor
類表示的是標準形式的Java Bean經過存取器(即get set方法)導出的一個屬性,好比,咱們能夠經過如下方式,對對象的屬性進行賦值:
public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public static void main(String[] args) throws Exception { Person test1 = new Person(); test1.setName("vvvv"); PropertyDescriptor pd = new PropertyDescriptor("name", test1.getClass()); Method setMethod = pd.getWriteMethod(); // 還有與Wirte方法對應的Read方法 setMethod.invoke(test1, "bbbbb"); System.out.print(test1); } }
Java中有strong、soft、weak、phantom
四種引用類型,下面介紹一下soft引用和weak引用:
Soft Reference
: 當對象是Soft reference
可達時,向系統申請更多內存,GC不是直接回收它,而是當內存不足的時候纔回收它。所以Soft reference適合用於構建一些緩存系統。Weak Reference
: 弱引用的強度比軟引用更弱一些,被弱引用關聯的對象只能生存到下一次GC發生以前。當垃圾收集器工做時,不管當前內存是否足夠,都會回收掉只被弱引用關聯的對象。private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { // 檢查source和target對象是否爲null,不然拋運行時異常 Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); // 獲取target對象的類信息 Class<?> actualEditable = target.getClass(); // 若editable不爲null,檢查target對象是不是editable類的實例,若不是則拋出運行時異常 // 這裏的editable類是爲了作屬性拷貝時限制用的 // 若actualEditable和editable相同,則拷貝actualEditable的全部屬性 // 若actualEditable是editable的子類,則只拷貝editable類中的屬性 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); for (PropertyDescriptor targetPd : targetPds) { // 獲取該屬性對應的set方法 Method writeMethod = targetPd.getWriteMethod(); // 屬性的set方法存在 且 該屬性不包含在忽略屬性列表中 if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { // 獲取source類相同名字的PropertyDescriptor, getPropertyDescriptor的具體實現看下方 PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { // 獲取對應的get方法 Method readMethod = sourcePd.getReadMethod(); // set方法存在 且 target的set方法的入參是source的get方法返回值的父類或父接口或者類型相同 // 具體ClassUtils.isAssignable()的實現方式請看下面詳解 if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { //get方法是不是public的 if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { //暴力反射,取消權限控制檢查 readMethod.setAccessible(true); } //獲取get方法的返回值 Object value = readMethod.invoke(source); // 原理同上 if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } // 將get方法的返回值 賦值給set方法做爲入參 writeMethod.invoke(target, value); } catch (Throwable ex) { throw new FatalBeanException( "Could not copy property '" + targetPd.getName() + "' from source to target", ex); } } } } } }
getPropertyDescriptors
源碼:
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException { // CachedIntrospectionResults類是對PropertyDescriptor的一個封裝實現,看forClass方法的實現 CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptors(); } @SuppressWarnings("unchecked") static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException { // strongClassCache的聲明以下: // strongClassCache = new ConcurrentHashMap<Class<?>, CachedIntrospectionResults>(64); // 即將Class做爲key,CachedIntrospectionResults做爲value的map, // 因爲線程安全的須要,使用ConcurrentHashMap做爲實現 CachedIntrospectionResults results = strongClassCache.get(beanClass); if (results != null) { return results; } // 若strongClassCache中不存在,則去softClassCache去獲取,softClassCache的聲明以下 // softClassCache = new ConcurrentReferenceHashMap<Class<?>, CachedIntrospectionResults>(64); // ConcurrentReferenceHashMap是Spring實現的能夠指定entry引用級別的ConcurrentHashMap,默認的引用級別是soft,能夠防止OOM results = softClassCache.get(beanClass); if (results != null) { return results; } results = new CachedIntrospectionResults(beanClass); ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse; // isCacheSafe方法檢查給定的beanClass是否由入參中的classloader或者此classloader的祖先加載的(雙親委派的原理) // isClassLoaderAccepted檢查加載beanClass的classloader是否在能夠接受的classloader的集合中 或者是集合中classloader的祖先 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; } // 根據classloader的結果,將類信息加載到對應的緩存中 CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results); return (existing != null ? existing : results); }
isAssignable
源碼:
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) { Assert.notNull(lhsType, "Left-hand side type must not be null"); Assert.notNull(rhsType, "Right-hand side type must not be null"); // 若左邊類型 是右邊類型的父類、父接口,或者左邊類型等於右邊類型 if (lhsType.isAssignableFrom(rhsType)) { return true; } // 左邊入參是不是基本類型 if (lhsType.isPrimitive()) { //primitiveWrapperTypeMap是從包裝類型到基本類型的map,將右邊入參轉化爲基本類型 Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType); if (lhsType == resolvedPrimitive) { return true; } } else { // 將右邊入參轉化爲包裝類型 Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType); if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) { return true; } } return false; }
ClassUtils.isAssignable()
方法擴展了Class的isAssignableFrom()
方法,即將Java
的基本類型和包裝類型作了兼容。
一個看似簡單的BeanUtils
工具類,其實裏面包含的Java基礎的知識點很是多,包括類型信息、反射、線程安全、引用類型、類加載器等。Spring
的BeanUtils
的實現裏使用了ConcurrentHashMap
做爲緩存,每次去獲取PropertyDescriptor
時,能夠直接去緩存裏面獲取,而沒必要每次都去調用native
方法,因此Spring
的BeanUtils
的性能仍是很不錯的。