成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Spring BeanUtils源碼分析

darkbug / 2587人閱讀

摘要:前置知識(shí)在分析源碼前,我們先溫習(xí)一下以下的知識(shí)點(diǎn)。類(lèi)在中萬(wàn)物皆對(duì)象,而且我們?cè)诖a中寫(xiě)的每一個(gè)類(lèi)也都是對(duì)象,是類(lèi)的對(duì)象??偨Y(jié)一個(gè)看似簡(jiǎn)單的工具類(lèi),其實(shí)里面包含的基礎(chǔ)的知識(shí)點(diǎn)非常多,包括類(lèi)型信息反射線程安全引用類(lèi)型類(lèi)加載器等。

背景

在我們著手一個(gè)Java Web項(xiàng)目的時(shí)候,經(jīng)常會(huì)遇到DO、VO、DTO對(duì)象之間的屬性拷貝,若采用get、set的方法來(lái)進(jìn)行賦值的話,代碼會(huì)相當(dāng)冗長(zhǎng)丑陋,一般我們會(huì)采用SpringBeanUtils類(lèi)來(lái)進(jìn)行屬性拷貝,其基本原理就是通過(guò)Java的反射機(jī)制,下面我們來(lái)看一下源碼的具體實(shí)現(xiàn)。

前置知識(shí)

在分析源碼前,我們先溫習(xí)一下以下的知識(shí)點(diǎn)。

java.lang.Class類(lèi)

在Java中萬(wàn)物皆對(duì)象,而且我們?cè)诖a中寫(xiě)的每一個(gè)類(lèi)也都是對(duì)象,是java.lang.Class類(lèi)的對(duì)象。所以,每個(gè)類(lèi)都有自己的實(shí)例對(duì)象,而且它們自己也都是Class類(lèi)的對(duì)象。

我們來(lái)看一下Class類(lèi)的構(gòu)造方法:

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類(lèi)的構(gòu)造方法是私有的,只有JVM可以創(chuàng)建該類(lèi)的對(duì)象,因此我們無(wú)法在代碼中通過(guò)new的方式顯示聲明一個(gè)Class對(duì)象。

但是,我們依然有其他方式獲得Class類(lèi)的對(duì)象:

1.通過(guò)類(lèi)的靜態(tài)成員變量

Class clazz = Test.class;

2.通過(guò)對(duì)象的getClass()方法

Class clazz = test.getClass();

3.通過(guò)Class的靜態(tài)方法forName()

// forName需要傳入類(lèi)的全路徑
Class clazz = Class.forName("destiny.iron.api.model.Test"); 
基本類(lèi)型和包裝類(lèi)型

基本類(lèi)型和其對(duì)應(yīng)的包裝類(lèi)的Class對(duì)象是不相等的,即long.class != Long.class 。

PropertyDescriptor類(lèi)

PropertyDescriptor類(lèi)表示的是標(biāo)準(zhǔn)形式的Java Bean通過(guò)存取器(即get set方法)導(dǎo)出的一個(gè)屬性,比如,我們可以通過(guò)以下方式,對(duì)對(duì)象的屬性進(jìn)行賦值:

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方法對(duì)應(yīng)的Read方法
        setMethod.invoke(test1, "bbbbb");
        System.out.print(test1);
    }
}
引用類(lèi)型

Java中有strong、soft、weak、phantom四種引用類(lèi)型,下面介紹一下soft引用和weak引用:

Soft Reference: 當(dāng)對(duì)象是Soft reference可達(dá)時(shí),向系統(tǒng)申請(qǐng)更多內(nèi)存,GC不是直接回收它,而是當(dāng)內(nèi)存不足的時(shí)候才回收它。因此Soft reference適合用于構(gòu)建一些緩存系統(tǒng)。

Weak Reference: 弱引用的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次GC發(fā)生之前。當(dāng)垃圾收集器工作時(shí),無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。

源碼分析
private static void copyProperties(Object source, Object target, Class editable, String... ignoreProperties)
            throws BeansException {
        // 檢查source和target對(duì)象是否為null,否則拋運(yùn)行時(shí)異常
        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");
        // 獲取target對(duì)象的類(lèi)信息
        Class actualEditable = target.getClass();
        // 若editable不為null,檢查target對(duì)象是否是editable類(lèi)的實(shí)例,若不是則拋出運(yùn)行時(shí)異常
        // 這里的editable類(lèi)是為了做屬性拷貝時(shí)限制用的
        // 若actualEditable和editable相同,則拷貝actualEditable的所有屬性
        // 若actualEditable是editable的子類(lèi),則只拷貝editable類(lèi)中的屬性
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                        "] not assignable to Editable class [" + editable.getName() + "]");
            }
            actualEditable = editable;
        }
        // 獲取目標(biāo)類(lèi)的所有PropertyDescriptor,getPropertyDescriptors這個(gè)方法請(qǐng)看下方
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        for (PropertyDescriptor targetPd : targetPds) {
            // 獲取該屬性對(duì)應(yīng)的set方法
            Method writeMethod = targetPd.getWriteMethod();
            // 屬性的set方法存在 且 該屬性不包含在忽略屬性列表中
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                // 獲取source類(lèi)相同名字的PropertyDescriptor, getPropertyDescriptor的具體實(shí)現(xiàn)看下方
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    // 獲取對(duì)應(yīng)的get方法
                    Method readMethod = sourcePd.getReadMethod();
                    // set方法存在 且 target的set方法的入?yún)⑹莝ource的get方法返回值的父類(lèi)或父接口或者類(lèi)型相同
                    // 具體ClassUtils.isAssignable()的實(shí)現(xiàn)方式請(qǐng)看下面詳解
                    if (readMethod != null &&
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            //get方法是否是public的
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                //暴力反射,取消權(quán)限控制檢查
                                readMethod.setAccessible(true);
                            }
                            //獲取get方法的返回值
                            Object value = readMethod.invoke(source);
                            // 原理同上
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            // 將get方法的返回值 賦值給set方法作為入?yún)?                            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類(lèi)是對(duì)PropertyDescriptor的一個(gè)封裝實(shí)現(xiàn),看forClass方法的實(shí)現(xiàn)
        CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
        return cr.getPropertyDescriptors();
    }
    
    @SuppressWarnings("unchecked")
    static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {
        // strongClassCache的聲明如下:
        // strongClassCache = new ConcurrentHashMap, CachedIntrospectionResults>(64);
        // 即將Class作為key,CachedIntrospectionResults作為value的map,
        // 由于線程安全的需要,使用ConcurrentHashMap作為實(shí)現(xiàn)
        CachedIntrospectionResults results = strongClassCache.get(beanClass);
        if (results != null) {
            return results;
        }
        // 若strongClassCache中不存在,則去softClassCache去獲取,softClassCache的聲明如下
        // softClassCache = new ConcurrentReferenceHashMap, CachedIntrospectionResults>(64);
        // ConcurrentReferenceHashMap是Spring實(shí)現(xiàn)的可以指定entry引用級(jí)別的ConcurrentHashMap,默認(rèn)的引用級(jí)別是soft,可以防止OOM
        results = softClassCache.get(beanClass);
        if (results != null) {
            return results;
        }

        results = new CachedIntrospectionResults(beanClass);
        ConcurrentMap, CachedIntrospectionResults> classCacheToUse;
        // isCacheSafe方法檢查給定的beanClass是否由入?yún)⒅械腸lassloader或者此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;
        }
        // 根據(jù)classloader的結(jié)果,將類(lèi)信息加載到對(duì)應(yīng)的緩存中
        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");
        // 若左邊類(lèi)型 是右邊類(lèi)型的父類(lèi)、父接口,或者左邊類(lèi)型等于右邊類(lèi)型
        if (lhsType.isAssignableFrom(rhsType)) {
            return true;
        }
        // 左邊入?yún)⑹欠袷腔绢?lèi)型
        if (lhsType.isPrimitive()) {
            //primitiveWrapperTypeMap是從包裝類(lèi)型到基本類(lèi)型的map,將右邊入?yún)⑥D(zhuǎn)化為基本類(lèi)型
            Class resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
            if (lhsType == resolvedPrimitive) {
                return true;
            }
        }
        else {
            // 將右邊入?yún)⑥D(zhuǎn)化為包裝類(lèi)型
            Class resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
            if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
                return true;
            }
        }
        return false;
    }

ClassUtils.isAssignable()方法擴(kuò)展了Class的isAssignableFrom()方法,即將Java的基本類(lèi)型和包裝類(lèi)型做了兼容。

總結(jié)

一個(gè)看似簡(jiǎn)單的BeanUtils工具類(lèi),其實(shí)里面包含的Java基礎(chǔ)的知識(shí)點(diǎn)非常多,包括類(lèi)型信息、反射、線程安全、引用類(lèi)型、類(lèi)加載器等。SpringBeanUtils的實(shí)現(xiàn)里使用了ConcurrentHashMap作為緩存,每次去獲取PropertyDescriptor時(shí),可以直接去緩存里面獲取,而不必每次都去調(diào)用native方法,所以SpringBeanUtils的性能還是很不錯(cuò)的。

原文鏈接

https://segmentfault.com/a/11...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71118.html

相關(guān)文章

  • 為什么阿里代碼規(guī)約要求避免使用 Apache BeanUtils 進(jìn)行屬性的拷貝

    摘要:于是我建議這位小伙伴使用了進(jìn)行屬性拷貝,這為我們的程序挖了一個(gè)坑阿里代碼規(guī)約當(dāng)我們開(kāi)啟阿里代碼掃描插件時(shí),如果你使用了進(jìn)行屬性拷貝,它會(huì)給你一個(gè)非常嚴(yán)重的警告。大名鼎鼎的提供的包,居然會(huì)存在性能問(wèn)題,以致于阿里給出了嚴(yán)重的警告。 聲明:本文屬原創(chuàng)文章,始發(fā)于公號(hào):程序員自學(xué)之道,并同步發(fā)布于 https://blog.csdn.net/dadiyang,特此,同步發(fā)布到 sf,轉(zhuǎn)載請(qǐng)注...

    raledong 評(píng)論0 收藏0
  • Apollo源碼分析(二): Apollo的代碼層次

    摘要:不同與其它中間件框架,中有大量的業(yè)務(wù)代碼,它向我們展示了大神是如何寫(xiě)業(yè)務(wù)代碼的依賴(lài)的層次結(jié)構(gòu),如何進(jìn)行基礎(chǔ)包配置,以及工具類(lèi)編寫(xiě),可以稱(chēng)之為之最佳實(shí)踐。代碼參考視圖解析器,這里的配置指的是不檢查頭,而且默認(rèn)請(qǐng)求為格式。 不同與其它中間件框架,Apollo中有大量的業(yè)務(wù)代碼,它向我們展示了大神是如何寫(xiě)業(yè)務(wù)代碼的:maven依賴(lài)的層次結(jié)構(gòu),如何進(jìn)行基礎(chǔ)包配置,以及工具類(lèi)編寫(xiě),可以稱(chēng)之為sp...

    cyqian 評(píng)論0 收藏0
  • BeanUtils工具使用細(xì)節(jié)

    摘要:拷貝操作又一個(gè)非常好用的工具類(lèi)和中分別存在一個(gè),提供了對(duì)。除了支持基本類(lèi)型以及基本類(lèi)型的數(shù)組之外,還支持這些類(lèi)的對(duì)象,其余一概不支持。而且,由于這些類(lèi)都是采用反射機(jī)制實(shí)現(xiàn)的,對(duì)程序的效率也會(huì)有影響。因此,慎用或者使用看效果如何 java bean拷貝操作又一個(gè)非常好用的工具類(lèi) BeanUitls :spring (org.springframework.beans.BeanUtils)...

    afishhhhh 評(píng)論0 收藏0
  • springboot源碼分析系列(二)--SpringApplication.run()啟動(dòng)流程

    摘要:眾所周知,類(lèi)上面帶有注解的類(lèi),即為的啟動(dòng)類(lèi)。一個(gè)項(xiàng)目只能有一個(gè)啟動(dòng)類(lèi)。根據(jù)是否是環(huán)境創(chuàng)建默認(rèn)的,通過(guò)掃描所有注解類(lèi)來(lái)加載和最后通過(guò)實(shí)例化上下文對(duì)象,并返回。 ??眾所周知,類(lèi)上面帶有@SpringBootApplication注解的類(lèi),即為springboot的啟動(dòng)類(lèi)。一個(gè)springboot項(xiàng)目只能有一個(gè)啟動(dòng)類(lèi)。我們來(lái)分析一下SpringBoot項(xiàng)目的啟動(dòng)過(guò)程,首先看看啟動(dòng)類(lèi)里面都包...

    adie 評(píng)論0 收藏0
  • Spring Boot 中 crud如何優(yōu)雅的實(shí)現(xiàn)-附代碼

    摘要:以下內(nèi)容基于如果你使用的也是相同的技術(shù)棧可以繼續(xù)往下閱讀,如果不是可以當(dāng)作參考。編寫(xiě)的四種方式裸寫(xiě)最簡(jiǎn)單最粗暴也是使用最多的一種方式,在寫(xiě)的多了之后可以用生成工具生成。 導(dǎo)讀 在目前接觸過(guò)的項(xiàng)目中大多數(shù)的項(xiàng)目都會(huì)涉及到: crud相關(guān)的操作, 哪如何優(yōu)雅的編寫(xiě)crud操作呢?帶著這個(gè)問(wèn)題,我們發(fā)現(xiàn)項(xiàng)目中大量的操作多是 創(chuàng)建實(shí)體 、刪除實(shí)例、 修改實(shí)體、 查詢單個(gè)實(shí)體、 分頁(yè)查詢多個(gè)實(shí)體...

    wing324 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<