Performance comparison BeanUtils object attributes and the copy source code analysis

1. A common way copy object properties and performances of

In everyday coding, often encounter convert between DO, DTO object if the attribute of the object itself is relatively small when we have hard-coded manual setter also ok, but if the properties of an object under more circumstances, it is hand-setter and low and inefficient. This time we consider a number of tools to copy the object attributes.

Our common way to copy object properties are:

  • Hard Code
  • net.sf.cglib.beans.BeanCopier#copy
  • org.springframework.beans.BeanUtils.copyProperties
  • org.apache.commons.beanutils.PropertyUtils.copyProperties
  • org.apache.commons.beanutils.BeanUtils.copyProperties

For more than a copy of the way, I made a simple performance testing results are as follows:

Copy format The number of objects: 1 The number of objects: 1000 The number of objects: 100000 The number of objects: 1000000
Hard Code 0 ms 1 ms 18 ms 43 ms
cglib.BeanCopier 111 ms 117 ms 107 ms 110 ms
spring.BeanUtils 116 ms 137 ms 246 ms 895 ms
apache.PropertyUtils 167 ms 212 ms 601 ms 7869 ms
apache.BeanUtils 167 ms 275 ms 1732 ms 12380 ms

Test environment: OS = macOS 10.14, = the CPU 2.5 GHz,Intel Core I7, Memory =16 GB, 2133MHz LPDDR3

Test Method: complex objects by a specified number to a copy, performed separately for each Case 10times the mean value
Version: commons-beanutils:commons-beanutils:1.9.3, org.springframework:spring-beans:4.3.5.RELEASE,cglib:cglib:2.2.2

Conclusion : From the test results it is clear that use Hard Codemanner Copy object attributes best performance; with net.sf.cglib.beans.BeanCopier#copythe way most stable copy object properties; and org.apache.commons.beanutils.BeanUtils.copyPropertiesthe way the data amount when the most powerful performance degradation. So when they encounter an object has more attributes in daily programming attribute copy priority uses net.sf.cglib.beans.BeanCopier#copy.

The reason of the above data generated huge gap in their different ways of realization of the principle and caused, Hard Code directly call the getter & settermethod value, cglibuses 字节码技术, and then three kinds are used in 反射the way. Differences generated when the excellent performance of the first two we all know, but why be reflected by way of the same attributes Copy it so great? This is what this paper we want to explore the content.

We first reading org.apache.commons.beanutils.BeanUtilsthe source code, followed by reading org.springframework.beans.BeanUtilsthe source code, and finally to demonstrate the performance difference through their respective implementation

apache.BeanUtilsAnd spring.BeanUtilsare reflective technology, also called Java Advanced API-- on reflection Introspector(introspection), so we must first understand Introspectorwhat it is.

2. Introspector

Introspector(内省)Jdk is provided for describing the Java beanproperties of the support, tool and method of the event; can be obtained by using such BeanInfoan object that implements the interface, BeanInfothe interface has two important ways:

  • BeanDescriptor getBeanDescriptor();

    BeanDescriptor It provides some java bean global information, such as the class type, class name, etc.

  • PropertyDescriptor[] getPropertyDescriptors()

    PropertyDescriptor It describes the java bean in a property and derive their getter & settermethodsSoftReference

Jdk introspection interfaces greatly simplifies the way the reflection of such information, we can easily call the java bean reflected by this group api. This group of soft api reference phantom reference to take advantage of the free memory; in certain places (e.g. declaredMethodCache) using caching api efficiency to speed, and this group is a thread-safe api.

Use:

BeanInfo beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor descriptor: descriptors) {
    Method readMethod = descriptor.getReadMethod();
    Method writeMethod = descriptot.getWriteMethod();
    // readMethod.invoke(...);
}

Above is about the Introspectorsimple understanding, let's first look at apache.BeanUtilsthe source code.

3. Source: apache.BeanUtils

apache.BeanUtilsIt is a static method contains a lot of tools, and almost all are static methods BeanUtilsBeansingleton object implementations provide. BeanUtilsBeanInlet JavaBean properties is a method of operation, it provides a single example of the external function. But there is a place different from a normal singleton: different class loaders have a different example, only one instance of each class loader , so there is actually a single embodiment of a pseudo-single embodiment singletion pseudo .

// ContextClassLoaderLocal对象管理了BeanUtilsBean的所有实例
private static final ContextClassLoaderLocal<BeanUtilsBean>
            BEANS_BY_CLASSLOADER = new ContextClassLoaderLocal<BeanUtilsBean>() {
                        @Override
                        protected BeanUtilsBean initialValue() {
                            return new BeanUtilsBean();
                        }
                    };
public static BeanUtilsBean getInstance() {
    return BEANS_BY_CLASSLOADER.get();
}
// {@link ContextClassLoaderLocal#get}
public synchronized T get() {
    valueByClassLoader.isEmpty();
    try {
        final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); // 获取当前线程的类加载器
        if (contextClassLoader != null) {
            T value = valueByClassLoader.get(contextClassLoader);
            if ((value == null)
                && !valueByClassLoader.containsKey(contextClassLoader)) {
                value = initialValue(); // 初始化BeanUtilsBean,即 new BeanUtilsBean();
                valueByClassLoader.put(contextClassLoader, value);
            }
            return value;
        }
    } catch (final SecurityException e) { /* SWALLOW - should we log this? */ }
    if (!globalValueInitialized) {
        globalValue = initialValue();
        globalValueInitialized = true;
    }
    return globalValue;
}

When to get BeanUtilsBeanafter instance, the next step is when we copy the object properties.

// omit exception
public static void copyProperties(final Object dest, final Object orig){
        BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

In the copyPropertiesmethod, the object for the type of original employ different logic:

  • Map : Map of matching by the dest Key attributes, then the assignment;
  • DynaBean: DynaBeanThe name implies, it is a dynamic java bean objects may be formed, i.e. it stores the internal name of the attribute type and the corresponding value is the attribute name and the attribute name inside the object corresponding to dest when copy attribute assignment;
  • 标准Java Bean : This is the type of our main analysis, it is standard JavaBean objects; the only difference between the two lies in the handle before the value of the original bean.
Steps of 3.1 attribute copy for the standard JavaBean
public void copyProperties(final Object dest, final Object orig) {
    // omit some code (省略一部分代码) ...
   final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig);
    for (PropertyDescriptor origDescriptor : origDescriptors) {
        final String name = origDescriptor.getName();
        if ("class".equals(name)) {
            continue; // No point in trying to set an object's class
        }
        if (getPropertyUtils().isReadable(orig, name) &&
            getPropertyUtils().isWriteable(dest, name)) {
            try {
                final Object value =
                    getPropertyUtils().getSimpleProperty(orig, name);
                copyProperty(dest, name, value);
            } catch (final NoSuchMethodException e) {
                // Should not happen
            }
        }
    }
}
  1. Depending on the type of parsing the original bean, which cachePropertyDescriptor
  2. Each of the original bean polling PropertyDescriptorto determine PropertyDescriptorwhether readable in the original bean, whether written in the target bean, only when two conditions are established with copy qualifications
  3. The PropertyDescriptoracquired corresponding values from the original bean, the property value corresponds to the copy target bean
Gets the PropertyDescriptor Bean 3.2
 final PropertyDescriptor[] origDescriptors =
                getPropertyUtils().getPropertyDescriptors(orig);

Gets PropertyDescriptorentrusted to PropertyUtilsBeanobject to implement:

public BeanUtilsBean() {
    this(new ConvertUtilsBean(), new PropertyUtilsBean());
}

PropertyUtilsBeanIs an API to operate using java reflection on Java Bean getter and setter methods, such code originally located BeanUtilsBeanin, but considering the reason code amount were isolated ( Much of this code was originally included in BeanUtils, but has been separated because of the volume of code involved).

In PropertyUtilsBeaneach of the Bean PropertyDescriptorwill be stored BeanIntrospectionData, every time the need to get the object PropertyDescriptor, the acquisition will start in cahche BeanIntrospectionData; if not, the API obtained by introspection BeanIntrospectionDataand placed in the cache:

private BeanIntrospectionData getIntrospectionData(final Class<?> beanClass) {
    // omit some check code ...
    BeanIntrospectionData data = descriptorsCache.get(beanClass);
    if (data == null) {
        data = fetchIntrospectionData(beanClass);
        descriptorsCache.put(beanClass, data);
    }
    return data;
}

private BeanIntrospectionData fetchIntrospectionData(final Class<?> beanClass) {
    final DefaultIntrospectionContext ictx = new DefaultIntrospectionContext(beanClass);
    for (final BeanIntrospector bi : introspectors) {
        try {
            bi.introspect(ictx);
        } catch (final IntrospectionException iex) {
            log.error("Exception during introspection", iex);
        }
    }
    return new BeanIntrospectionData(ictx.getPropertyDescriptors());
}

In the fetchIntrospectionData()process, the built-in device introspection DefaultBeanIntrospectorinformation using introspection java API to transfer acquired DefaultIntrospectionContext, through DefaultIntrospectionContextconfiguration BeanIntrospectionData. DefaultBeanIntrospectorSpecific code:

public void introspect(final IntrospectionContext icontext) {
    BeanInfo beanInfo = null;
    try {
        // JAVA 的 Instrospector
        beanInfo = Introspector.getBeanInfo(icontext.getTargetClass());
    } catch (final IntrospectionException e) {
        return;
    }
    PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
    if (descriptors == null) {
    descriptors = new PropertyDescriptor[0];
    }
    // 解决IndexedPropertyDescriptor在不同版本的JDK下的差异
    handleIndexedPropertyDescriptors(icontext.getTargetClass(), descriptors);
    icontext.addPropertyDescriptors(descriptors);
}
3.3 is determined whether the attribute readable / writable

To attribute copy, it must first ensure that the properties of the original object is readable, writable target object property. In PropertyUtilsBeanthrough the isWriteable(); isReadable()method, the two methods seem long, we deal with on exception is omitted and out of view under:

public boolean isReadable(Object bean, String name) {
    // Omit Validate method parameters
    // Resolve nested references, 解析内嵌的属性,形如 student.name 
    while (resolver.hasNested(name)) {
        final String next = resolver.next(name);
        Object nestedBean = nestedBean = getProperty(bean, next);
        if (nestedBean == null) {
            throw new NestedNullException("Null property value for);
        }
        bean = nestedBean;
        name = resolver.remove(name);
    }
    // Remove any subscript from the final name value, 在最终的方法名中移除所有的下标
    name = resolver.getProperty(name);
    if (bean instanceof WrapDynaBean) {
        bean = ((WrapDynaBean)bean).getInstance();
    }
    if (bean instanceof DynaBean) {
        // All DynaBean properties are readable,所有DynaBean的属性均是可读的
        return (((DynaBean) bean).getDynaClass().getDynaProperty(name) != null);
    } else {
        final PropertyDescriptor desc = getPropertyDescriptor(bean, name);
        if (desc != null) {
            Method readMethod = getReadMethod(bean.getClass(), desc);
            if (readMethod == null) {
                if (desc instanceof IndexedPropertyDescriptor) {
                    readMethod = ((IndexedPropertyDescriptor) desc).getIndexedReadMethod();
                } else if (desc instanceof MappedPropertyDescriptor) {
                    readMethod = ((MappedPropertyDescriptor) desc).getMappedReadMethod();
                }
                readMethod = MethodUtils.getAccessibleMethod(bean.getClass(), readMethod);
            }
            return (readMethod != null);
        } else {
            return (false);
        }
    }
}

From the above we can see that the code, read each attribute may need to write acquired Method each use, and then determination, and also requires the processing DynaBean, Nested logic; when we Copy bulk properties, still need to perform the above steps, the result of the determination method will not be cached, and this is the reason that compared to the other jar inefficient.

3.4 Bean read the property value of the original, set the property value of the target Bean's

We omit the code to determine the validity and unusual of them:

public Object getSimpleProperty(final Object bean, final String name)
            throws IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
    // omit check null code ...
    // 校验属性
    if (resolver.hasNested(name)) {
        throw new IllegalArgumentException
                ("Nested property names are not allowed: Property '" +
                name + "' on bean class '" + bean.getClass() + "'");
    } else if (resolver.isIndexed(name)) {
        throw new IllegalArgumentException
                ("Indexed property names are not allowed: Property '" +
                name + "' on bean class '" + bean.getClass() + "'");
    } else if (resolver.isMapped(name)) {
        throw new IllegalArgumentException
                ("Mapped property names are not allowed: Property '" +
                name + "' on bean class '" + bean.getClass() + "'");
    }

    // DynaBean的特殊逻辑
    if (bean instanceof DynaBean) {
        final DynaProperty descriptor =
                ((DynaBean) bean).getDynaClass().getDynaProperty(name);
        if (descriptor == null) {
            throw new NoSuchMethodException("Unknown property '" +
                    name + "' on dynaclass '" +
                    ((DynaBean) bean).getDynaClass() + "'" );
        }
        return (((DynaBean) bean).get(name));
    }

    final PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
    if (descriptor == null) {
        throw new NoSuchMethodException("Unknown property '" +
                name + "' on class '" + bean.getClass() + "'" );
    }
    // 获取getter方法
    final Method readMethod = getReadMethod(bean.getClass(), descriptor);
    if (readMethod == null) {
        throw new NoSuchMethodException("Property '" + name +
                "' has no getter method in class '" + bean.getClass() + "'");
    }
    // 调用getter方法读取值
    final Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
    return (value);
}

The above is a method to read the property value. After reading the property value is set to the target value of the bean. In BeanUtilsBeanimplementation, the repeated processing logic and embedded logic DynaBean property, ultimately obtained which impart the target value setter method Bean.

4. Source: spring.BeanUtils

BeanUtilsLocated spring-beansmodule exposes static methods copyPropertiesfor performing attribute copy, each copyPropertieseventually have to call a private static method implemented properties copy:

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties){
        Assert.notNull(source, "Source must not be null");
        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;
    }
    // 第一步 调用Java 内省API 获取PropertyDescriptor
    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
    List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
    // 第二步 轮询目标bean的PropertyDescriptor
    for (PropertyDescriptor targetPd : targetPds) {
        Method writeMethod = targetPd.getWriteMethod();
        // 判断是否存在setter方法以及属性是否在需要忽略的属性列表中
        if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
            // 获取源bean的PropertyDescriptor
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) {
                // 获取getter方法
                Method readMethod = sourcePd.getReadMethod();
                if (readMethod != null &&
                        ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                    try {
                        // 如果getter方法不是public,则需要设置其accessible
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        // 反射获取属性值
                        Object value = readMethod.invoke(source);
                        // 如果setter方法不是public则需要设置其accessible
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        // 反射赋值
                        writeMethod.invoke(target, value);
                    }
                    catch (Throwable ex) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }
            }
        }
    }
}
Gets the PropertyDescriptor Bean 4.1

spring.BeanUtilsFor the bean in the PropertyDescriptorprocess and the cache are made CachedIntrospectionResultsfor processing class. CacheIntrospectionResultsThe static data cached in the collection, a method using a design pattern of the plant, by forClass(Class)methods expose cache:

static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
    // 从缓存中获取CachedIntrospectionResults
    CachedIntrospectionResults results = strongClassCache.get(beanClass);
    if (results != null) {
        return results;
    }
    // 从缓存中获取CachedIntrospectionResults
    results = softClassCache.get(beanClass);
    if (results != null) {
        return results;
    }
    // 构造CachedIntrospectionResults
    results = new CachedIntrospectionResults(beanClass);
    ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse;
    // 选取对应的缓存
    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;
    }
    CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
    return (existing != null ? existing : results);
}

We can see here have two cache: strongClassCacheand softClassCachethat They what difference does it?

First we look at their definitions:

static final ConcurrentMap<Class<?>, CachedIntrospectionResults> strongClassCache =
            new ConcurrentHashMap<Class<?>, CachedIntrospectionResults>(64);
static final ConcurrentMap<Class<?>, CachedIntrospectionResults> softClassCache =
            new ConcurrentReferenceHashMap<Class<?>, CachedIntrospectionResults>(64);

ConcurrentReferenceHashMap can specify the reference level corresponding to that segment locks for internal use, with the principles of jdk1.7 ConcurrentMap similar implementation.

strongClassCacheIs held in the cache 强引用, and the softClassCacheholding cache is 软引用( the JDK has 4 levels of reference, references are strong, soft, weak, and reference phantom reference, reference examples reflected when determining the level held by GC recovered timing ).

strongClassCacheFor caching cache-safethe bean class data, and softClassCachefor caching none-cache-safebean class data; strongClassCacheconsistent with data in the spring file application life cycle, softClassCachelife cycle managed not by the spring, so in order to prevent lead classloader prematurely closed 内存泄漏, where soft reference cache.

What kind of data is in the cache strongClassCachein it? beanClass ClassLoader is the same as the current program or the specified ClassLoader will be stored in the same strongClassCache, the rest are stored softClassCachein.

If the data does not get more from the cache, so will new CachedIntrospectionResults(Class)the corresponding call the relevant API Java Introspector ARE constructor:

// omit some logger code ...
private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
    try {
        BeanInfo beanInfo = null;
        // 对一些特殊的set方法(setA(int index, Object a))或者list的set方法进行处理
        for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
            beanInfo = beanInfoFactory.getBeanInfo(beanClass);
            if (beanInfo != null) {
                break;
            }
        }
        if (beanInfo == null) {
            // fall back到默认获取BeanInfo的方式
            beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
                    Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) : Introspector.getBeanInfo(beanClass));
        }
        this.beanInfo = beanInfo;
        // propertyDescriptor缓存
        this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();

        // 考虑到性能原因,对于每个PropertyDescriptor只处理一次
        PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : pds) {
            if (Class.class == beanClass &&
                    ("classLoader".equals(pd.getName()) ||  "protectionDomain".equals(pd.getName()))) {
                continue;
            }
            // 重新包装为GenericTypeAwarePropertyDescriptor
            pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
            this.propertyDescriptorCache.put(pd.getName(), pd);
        }

        // 检查Java 8在接口中的默认实现方法
        Class<?> clazz = beanClass;
        while (clazz != null) {
            Class<?>[] ifcs = clazz.getInterfaces();
            for (Class<?> ifc : ifcs) {
                BeanInfo ifcInfo = Introspector.getBeanInfo(ifc, Introspector.IGNORE_ALL_BEANINFO);
                PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors();
                for (PropertyDescriptor pd : ifcPds) {
                    if (!this.propertyDescriptorCache.containsKey(pd.getName())) {
                        pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
                        this.propertyDescriptorCache.put(pd.getName(), pd);
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        this.typeDescriptorCache = new ConcurrentReferenceHashMap<PropertyDescriptor, TypeDescriptor>();
    }
    catch (IntrospectionException ex) {
        throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
    }
}

The main role of this code is to get through introspection BeanInfo interface and then PropertyDescriptorcached. specific process:

  1. First get BeanInfo by BeanInfoFactory; here by default when registering BeanInfoFactoryShi ExtendedBeanInfoFactory, such a major deal includes some special setmethods of bean:
public static boolean isCandidateWriteMethod(Method method) {
       String methodName = method.getName();
       Class<?>[] parameterTypes = method.getParameterTypes();
       int nParams = parameterTypes.length;
       return (methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
                (!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
                (nParams == 1 || (nParams == 2 && int.class == parameterTypes[0])));
   }

If a bean is included in such a method: to setbegin with && (return value is not void|| static method) && (|| having a parameter which has two parameters is the first parameter int), the form:

   // void.class.isAssignableFrom(method.getReturnType()) 方法返回值不为void
   public Bean setFoo(Foo foo) {
       this.foo = foo;
       return this;
   }
   public static void setFoos(Foo foo) {
       Bean.foo = foo;
   }
   public Bean setFoos(int index, Foo foo) {
       this.foos.set(index, foo);
       return this;
   }
  1. If the bean does not contain the above-described method, using the Java introspection directly acquired APIBeanInfo

  2. When to get BeanInfoafter it can PropertyDescriptorbe cached; this will be PropertyDescriptorrepackaged as GenericTypeAwarePropertyDescriptor, Reasons for this package is to reprocess BridgeMethod , popular speak, is to deal with the current class inherits the generic class or implement generic interface, then how recognition of these methods?

    Bridge Method: Bridging method is the introduction of a generic jdk jdk for compatibility with previous versions, automatically generated at compile time method. Flag bridging method bytecode will be labeled as ACC_BRIDGE(bridge method) and ACC_SYNTHETIC(generated by the compiler). Through Method.isBridge()to a method for determining whether a BridgeMethod. If a method overrides a generic parent or a generic interface implemented is generated bridge method.

public static Method findBridgedMethod(Method bridgeMethod) {
    if (bridgeMethod == null || !bridgeMethod.isBridge()) {
        return bridgeMethod;
    }
    // 获取所有与bridgeMethod名称、参数数量相匹配的方法(包括父类)
    List<Method> candidateMethods = new ArrayList<Method>();
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
    for (Method candidateMethod : methods) {
           // candidateMethod是`Bridge Method`时将其加入候选方法列表
        if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
            candidateMethods.add(candidateMethod);
        }
    }
    if (candidateMethods.size() == 1) {
        return candidateMethods.get(0);
    }
    // 在众候选方法中找到其BridgeMethod,如果找不到返回原方法
    Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
    if (bridgedMethod != null) {
        return bridgedMethod;
    }else {
        return bridgeMethod;
    }
   }
  1. Method processed class, interface implemented method must deal with. In Java8, the interface can have a default method, for example:
   public interface MethodAvailable {
       default String getHello(){
           return "hello";
       }
       String setHello(String hello);
   }

Consistent with processing logic and processing logic interface class for the method implemented in the implementation of the method.

After completion of the above procedure, we got the result that buffers introspection CachedIntrospectionResultsexample, and select the corresponding cahche, the results cached together. (Cahce selection process previously described process of reading cache consistency);

4.2 copy property values

Acquired from the cache to the target class PropertyDescriptor, the polling each of which will PropertyDescriptorassign a.

The assignment process is relatively simple point:

  1. Write method of acquiring the target class ( setter)
  2. If the write method in the target is not empty and the method in the corresponding attribute is not configured igonreList(ignoring attribute list), the method reads the corresponding attribute is acquired source class ( getter)
  3. After acquiring the reading method, reading method needs to determine whether the return parameter values ​​and write method is the same type, different types of course can not copy the
  4. Reading method to determine whether public, and if not, you need to set access permissions method.setAccessible(true);(non-public method to access when you need to set the reflection setAccessible(true)to gain access), and then call the reflective perform this method invoke(source);
  5. Write method to determine whether public, if it is not set access permissions, and then read the value assigned by the radiation target class invoke(taget, value);

At this point, the class attribute copy is complete.

5. Summary

For performance reasons and apache.BeanUtils the difference between 5.1 spring.BeanUtils

When a large number of copy, apache.BeanUtilscompared to spring.BeanUtilsslow in recent 14times, the reason, in fact, is the following:

  • apache.BeanUtilsIn each class loader implements a cache BeanUtilsBeaninstance, on getting this instance will lock ( synchronized)
  • apache.BeanUtilsSupport DynaBeanand Mapability to Object mapping, but for the post- PropertyDescriptorprocessing, even though I used a simple Object, also to judge DynaBeanand Map, where if the policy mode to separate it should reduce a lot of time to judge
  • apache.BeanUtilsEach time the property copy execution, from re- PropertyDescriptoracquiring reading and writing method, although PropertyDescriptoris cached, but every acquisition readMethod/writeMethodis also very time-consuming, especially when a larger number of object instances, if here for readMethod/writeMethodcache, performance should be many upgrade
  • In contrast spring.BeanUtilsreason than apache.BeanUtilsfast, is its PropertyDescriptorfirst cache only after treatment. In contrast visible to PropertyDescriptorthe process it is very time-consuming.
5.2 Harvest

Through this inquiry, we learned the following knowledge:

  1. Java IntrospectprIn time of reflection before use, and are based on primitive methods to obtain information and cache then the Map; such defects is when the different modules require reflection, if the result of poor communication leads to another man by when the raw reflected interface to obtain class information, the cache can not be utilized; then using introspection, JDK default cache.
  2. Bridge MethodBefore generics erased understanding only stay at compile generics will be erased, understand bridge method, the mechanism for generics has also been more understanding
  3. When the properties of each copy mode usage scenarios:
    1. For higher performance requirements when the recommended method is called manual
    2. General recommended scenarionet.sf.cglib.beans.BeanCopier#copy
    3. If you take into account the risk of introducing new jar package recommendedorg.springframework.beans.BeanUtils.copyProperties

Guess you like

Origin www.cnblogs.com/kancy/p/12089126.html