パフォーマンス比較々BeanUtilsオブジェクト属性とコピーソースコード解析

1. A一般的な方法のコピーオブジェクトのプロパティとの性能

日常では、我々はまた、[OK]をハードコーディングされたマニュアルセッターを持っているときに、オブジェクト自体の属性が比較的小さい場合が多い、DO、DTOオブジェクト間の出会いの変換をコーディングするが、より多くの状況下でのオブジェクトのプロパティの場合それは手セッターと低い、非効率的です。今回は、オブジェクトの属性をコピーするツールの数を考慮してください。

オブジェクトのプロパティをコピーするために私たちの一般的な方法です。

  • 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

道のコピーよりも多くの場合、私は結果をテストする簡単なパフォーマンスをしたとして、次のとおりです。

コピーフォーマット オブジェクトの数:1 オブジェクトの数:1000 オブジェクトの数:100000 オブジェクトの数:1000000
Hard Code 0ミリ秒 1ミリ秒 18ミリ秒 43ミリ
cglib.BeanCopier 111ミリ秒 117ミリ秒 107ミリ秒 110ミリ秒
spring.BeanUtils 116ミリ秒 137ミリ秒 246ミリ秒 895ミリ秒
apache.PropertyUtils 167ミリ秒 212ミリ秒 601ミリ秒 7869ミリ秒
apache.BeanUtils 167ミリ秒 275ミリ秒 1732ミリ秒 12380ミリ秒

テスト環境:OS = macOS 10.14、= CPU 2.5 GHz,Intel Core I7、メモリー=16 GB, 2133MHz LPDDR3

試験方法:コピーに指定された数だけ複雑なオブジェクトは、各ケースごとに個別に実行10時間の平均値を
バージョン:commons-beanutils:commons-beanutils:1.9.3org.springframework:spring-beans:4.3.5.RELEASEcglib:cglib:2.2.2

結論:使用することを、それは明らかであるテストの結果から、Hard Code仕方コピーオブジェクトが最高のパフォーマンスを属性;とnet.sf.cglib.beans.BeanCopier#copyして、最も安定したコピーオブジェクトのプロパティウェイorg.apache.commons.beanutils.BeanUtils.copyPropertiesウェイデータ量の最も強力なパフォーマンスの低下。彼らが遭遇したときにオブジェクトは、毎日のプログラミング属性のコピーではより多くの属性があり、優先度の使用をnet.sf.cglib.beans.BeanCopier#copy

上記のデータの理由が原則と原因の実現の彼らの異なる方法で大きなギャップを生成し、ハードコードは、直接呼び出すgetter & setterメソッド値、cglib用途が字节码技术、その後、3種類の中で使用されている反射方法。優れた最初の2人の我々はすべて知っているのパフォーマンスが、なぜ同じ属性の方法によって反射さはそれほど大きく、それをコピーしたときに発生する違い?これは、この論文は、我々はコンテンツを探索したいものです。

私たちはまず読んでorg.apache.commons.beanutils.BeanUtils、ソースコードを読むことによって、その後org.springframework.beans.BeanUtilsのソースコードを、そして最後に、それぞれの実施を通じて、パフォーマンスの違いを示すために、

apache.BeanUtilsそして、spring.BeanUtils反射技術は、また、反射上のJavaの詳細API--と呼ばれているIntrospector(イントロスペクション)、私たちはまず理解しなければならないIntrospector、それが何でありますか。

2.イントロスペクター

Introspector(内省)JDKを説明するために提供されるJava beanイベントの支持体、工具及び方法の特性を、そのような使用によって得ることができるBeanInfo実装インタフェースそのオブジェクトを、BeanInfoインターフェースは、2つの重要な方法があります。

  • BeanDescriptor getBeanDescriptor();

    BeanDescriptor これは、などクラス型、クラス名、など、いくつかのJava Beanのグローバルな情報を提供します

  • PropertyDescriptor[] getPropertyDescriptors()

    PropertyDescriptor これは、プロパティでJava Beanを説明し、その導出getter & setter方法をSoftReference

JDKのイントロスペクション・インターフェースを大幅にそのような情報の反射は、我々は簡単にこのグループのAPIにより反射されたJava Beanを呼び出すことができる方法を簡略化します。ソフトAPI基準ファントム参照のこのグループは、空きメモリを利用するために、特定の場所(例えば内declaredMethodCache)速度にキャッシングAPI効率を使用して、このグループのスレッドセーフなAPIがあります。

使用します。

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

上記は、についてですIntrospectorでの最初のを見てみましょう、単純な理解apache.BeanUtilsのソースコードを。

3.出典:apache.BeanUtils

apache.BeanUtilsこれは、静的メソッドは、ツールの多くが含まれており、ほとんどすべての静的メソッドでBeanUtilsBean実装が提供するオブジェクトシングルトン。BeanUtilsBean入口JavaBeanプロパティ操作する方法であって、外部関数の単一の例を提供します。しかし、通常シングルトンから場所は異なるがある:別のクラスローダーは、各クラスローダのインスタンスを1つだけ異なる例を持っているので、単一の実施形態、実際に存在する擬似単一の実施形態のsingletionの擬似

// 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;
}

取得する場合はBeanUtilsBean、インスタンスの後、我々は、オブジェクトのプロパティをコピーする場合、次のステップです。

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

copyProperties方法は、元採用異なる論理のタイプのオブジェクト。

  • Map :その後、DESTキー属性、譲渡することによりマッチングの地図。
  • DynaBeanDynaBean名前が意味する、それはすなわち、それは属性タイプの内部名を格納し、対応する値がDEST場合コピー属性に対応するオブジェクト内の属性名と属性名であり、ダイナミックのJava Beanオブジェクトを形成してもよいです割り当て;
  • 标准Java Bean :これは私たちの主な分析の一種であり、それは標準のJavaBeanオブジェクトであり、本来の豆の値の前にハンドル内の2つの嘘の唯一の違い。
3.1属性のステップは、標準の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. オリジナルの豆を解析の種類に応じて、どのキャッシュPropertyDescriptor
  2. 原豆ポーリングのそれぞれがPropertyDescriptor決定するPropertyDescriptor2つの条件がコピー資格との間で確立されている場合にのみ、ターゲットBeanに書き込まれたかどうか、元のBean内か可読
  3. PropertyDescriptor原豆、コピー対象Beanへのプロパティ値の対応から取得した対応する値
PropertyDescriptorビーン3.2を取得します
 final PropertyDescriptor[] origDescriptors =
                getPropertyUtils().getPropertyDescriptors(orig);

取得しPropertyDescriptorに委託PropertyUtilsBean実装するオブジェクト:

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

PropertyUtilsBeanJava BeanがゲッターとセッターメソッドにJavaリフレクションを使用して動作させるためのAPIで、もともと設置ようなコードBeanUtilsBean(ではなく、符号量を単離した理由を考えますMuch of this code was originally included in BeanUtils, but has been separated because of the volume of code involved)。

PropertyUtilsBean豆の各PropertyDescriptor格納されるBeanIntrospectionDataオブジェクトを取得するために、毎回必要性PropertyDescriptor、買収はcahcheで起動しますBeanIntrospectionData。ない場合は、APIは、イントロスペクションによって得られたBeanIntrospectionDataし、キャッシュに置か:

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());
}

fetchIntrospectionData()プロセス、内蔵デバイスイントロスペクションDefaultBeanIntrospector情報を転送するのイントロスペクションのJava APIを用いて取得DefaultIntrospectionContextを通して、DefaultIntrospectionContext構成BeanIntrospectionDataDefaultBeanIntrospector特定のコード:

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は、属性の読み出し/書き込み可能か否かが判断されます

属性コピーするには、まず元のオブジェクトのプロパティが読み取り、書き込み可能なターゲットオブジェクトのプロパティであることを確認する必要があります。ではPropertyUtilsBeanによるisWriteable(); isReadable()方法、2つの方法が長いようで、我々は例外に対処下を省略し、ビューのアウトされています。

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);
        }
    }
}

我々上記のコードは、各属性を読み取ることがわかりますから取得方法の各使用を記述する必要があり、その後、決意を、また、ネストされたロジックは、処理DynaBeanを必要とすることができ、我々は、バルク特性をコピーする場合、まだ上記の手順を実行する必要があり、決意の方法の結果はキャッシュされません、これは非効率的な他のジャーに比べことが理由です。

3.4ビーンが対象Beanのプロパティの値を設定し、元のプロパティの値を読み取ります

私たちは、それらの有効性や珍しいを決定するためのコードを省略します。

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);
}

上記は、プロパティ値を読み取るための方法です。プロパティ値を読み取った後、Beanの目標値に設定されています。BeanUtilsBean実装、最終的に目標値設定方法豆を与えるが得られ、論理および埋め込みロジックDynaBeanプロパティの処理を繰り返します。

4.出典:spring.BeanUtils

BeanUtils位置spring-beansモジュールは、静的メソッド公開するcopyProperties属性のコピーを実行するために、それぞれがcopyProperties最終的に実装プロパティをコピープライベート静的メソッドを呼び出す必要があります:

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);
                    }
                }
            }
        }
    }
}
PropertyDescriptorビーン4.1を取得します

spring.BeanUtils内のBeanのためPropertyDescriptorのプロセスおよびキャッシュ作られCachedIntrospectionResultsたクラスを処理します。CacheIntrospectionResultsコレクションにキャッシュされた静的データ、によって、植物のデザインパターンを使用する方法forClass(Class)方法には、キャッシュを公開:

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);
}

私たちは、2つのキャッシュを持ってこちらを見ることができますstrongClassCacheし、softClassCache彼らはどのような違いがそれをしないこと?

まず、その定義を見て:

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

ConcurrentReferenceHashMapはjdk1.7のConcurrentMap同様の実装の原理と、内部使用のために、そのセグメント・ロックに対応する基準レベルを指定することができます。

strongClassCacheキャッシュに保持され强引用、かつsoftClassCache保持キャッシュがある软引用回収GCによって保持されたレベルを決定する際にJDKが参照は、柔らかい、弱い強力であり、基準ファントム参照、参照の4つのレベルを持ち、参考例は、反射しましたタイミング)。

strongClassCacheキャッシュするためのcache-safe、およびBeanクラスデータをsoftClassCacheキャッシュするnone-cache-safeBeanクラスのデータを、strongClassCache一貫性のある春のファイルアプリケーションのライフサイクルのデータと、softClassCacheライフサイクルが早まって閉じたので、リードクラスローダを防ぐために、スプリングによってません管理し内存泄漏、どこソフト参照キャッシュ。

キャッシュ内のデータのどのようなstrongClassCacheことでは?たbeanClassのClassLoaderが同じに格納される現在のプログラムまたは指定されたクラスローダと同じでありstrongClassCache、残りが格納されてsoftClassCacheいます。

データはキャッシュからより取得していない、そうする場合はnew CachedIntrospectionResults(Class)、対応するコール関連のAPIのJava Introspectorのコンストラクタされています。

// 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);
    }
}

このコードの主な役割は、イントロスペクションのBeanInfoインタフェースとを介して取得することでPropertyDescriptor、キャッシュされました。特定のプロセス:

  1. まずBeanInfoFactoryでのBeanInfoを取得する。ここでは、デフォルトで登録するときにBeanInfoFactory市がExtendedBeanInfoFactory、そのような主要な取引はいくつかの特別な含まsetBeanのメソッドを:
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])));
   }

豆は、そのような方法に含まれている場合:するset&&で始まる(戻り値がないvoid||静的方法)&&(|| 2つのパラメータがあり、パラメータを有する第一のパラメータであるint)、形式:

   // 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. 豆は、Javaイントロスペクションを直接取得APIを使用して、上記の方法が含まれていない場合BeanInfo

  2. 取得する場合はBeanInfo、それができた後PropertyDescriptorにキャッシュされ、これがされるPropertyDescriptorように再パッケージGenericTypeAwarePropertyDescriptor、このパッケージの理由がすることですBridgeMethodを再処理し、人気話すは、ジェネリッククラスを継承現在のクラスに対処または汎用インタフェースを実装することで、その後、どのようにこれらのメソッドの認識?

    Bridge Method:ブリッジング方法は、自動的にコンパイル時法で発生以前のバージョンとの互換性のための一般的なJDKのJDKの導入です。メソッドのバイトコードを架橋フラグとして標識されるACC_BRIDGE(ブリッジ法)及びACC_SYNTHETIC(コンパイラによって生成されます)。スルーMethod.isBridge()かどうかを決定するための方法に関するBridgeMethodこの方法は、一般的な親をオーバーライドする場合や、実装の汎用インタフェースが生成されます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. この方法は、インタフェース実装方法が対処しなければならない、クラスを処理しました。Java8では、インタフェースは例えば、デフォルトのメソッドを持つことができます。
   public interface MethodAvailable {
       default String getHello(){
           return "hello";
       }
       String setHello(String hello);
   }

ロジックを処理し、メソッドの実装に実装された方法のための論理インターフェースクラスの処理と一致します。

上記の手順が完了した後、我々は結果そのバッファのイントロスペクションなったCachedIntrospectionResults例を、対応するcahcheを選択し、結果がキャッシュされて一緒に。(Cahce選択プロセスは、以前にキャッシュ一貫性の読み取り処理を説明します)。

4.2コピーのプロパティ値

ターゲットクラスにキャッシュから取得したPropertyDescriptor、しますそれぞれのポーリングPropertyDescriptor割り当てます。

割り当てプロセスは比較的単純なポイントです。

  1. ターゲットクラスを取得するWriteメソッド(setter
  2. 対象における書き込み方法が空でないと対応する属性のメソッドが設定されていない場合igonreList(属性リストを無視して)、この方法は、(対応する属性は、ソースクラスを取得する読み出しgetter
  3. 読み取り方法を取得した後、メソッドのニーズを読み取りが戻りパラメータ値と書き込み方法は同じタイプであるかどうかを判断するために、当然の異なる種類をコピーすることはできません
  4. かどうかを判断するための方法を読んでpublic、そうでない場合、あなたはアクセス権限を設定する必要があるmethod.setAccessible(true);(あなたが反射を設定する必要がある場合にアクセスする非パブリックメソッドsetAccessible(true)ゲインアクセスに)、その後、この方法を実行する反射を呼び出しますinvoke(source)
  5. かどうかを決定するために、書き込み方法public、それがアクセス権を設定し、放射線ターゲットクラスによって割り当てられた値を読み取るされていない場合、invoke(taget, value)

この時点で、クラス属性のコピーが完了しています。

5.まとめ

パフォーマンス上の理由とapache.BeanUtils 5.1 spring.BeanUtilsの違いのために

コピーの数が多い、ときapache.BeanUtilsと比べてspring.BeanUtils、最近では遅く14回、その理由は、実際には、次のとおりです。

  • apache.BeanUtils各クラスローダの実装では、キャッシュ・BeanUtilsBeanインスタンスは、このインスタンスを得る上で(ロックされますsynchronized
  • apache.BeanUtilsサポートDynaBeanMap能力オブジェクトへのマッピングが、ポストのためのPropertyDescriptor処理を、私はシンプルなオブジェクトを使用していても、また、裁判官にDynaBeanしてMap、それを分離するためのポリシーモードが裁判官に多くの時間を減らす必要がある場合は、
  • apache.BeanUtilsたびに再からプロパティコピー実行、PropertyDescriptor、読書を取得し、メソッドを記述がPropertyDescriptorキャッシュされているが、すべての買収は、readMethod/writeMethodオブジェクトインスタンスの数が多いほど、ここのための場合場合は特に、また、非常に時間がかかり、readMethod/writeMethodキャッシュ、パフォーマンスをする必要があります多くのアップグレード
  • コントラストでspring.BeanUtilsより理由apache.BeanUtils速く、そのあるPropertyDescriptorだけで処理した後の最初のキャッシュが。見える対照的に、PropertyDescriptorプロセスは非常に時間がかかります。
5.2収穫

この問い合わせを通じ、我々は次のような知識を学びました:

  1. Java Introspectpr使用前に反射の際に、情報と、キャッシュ地図を取得するプリミティブの方法に基づいて、異なるモジュールが反射を必要とする場合、このような欠陥がある場合、別の人への通信不良リードによる結果クラス情報を取得する際に生反射インタフェース、キャッシュが利用できない、次にイントロスペクション、JDKのデフォルトのキャッシュを使用。
  2. Bridge Methodコンパイルジェネリック医薬品に泊まるだけ理解消去ジェネリック医薬品が消去される前に、理解しbridge method、ジェネリック医薬品のためのメカニズムは、より理解されています
  3. 場合は、各コピーモードの使用シナリオのプロパティ:
    1. 高いパフォーマンス要件にお勧めの方法は、マニュアル呼び出されたとき
    2. 一般的な推奨シナリオnet.sf.cglib.beans.BeanCopier#copy
    3. あなたが考慮に入れるならば、新しいjarファイルのパッケージを導入するリスクが推奨しますorg.springframework.beans.BeanUtils.copyProperties

おすすめ

転載: www.cnblogs.com/kancy/p/12089126.html