<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 4, 文章 - 0, 評論 - 2, 引用 - 0
    數(shù)據(jù)加載中……

    Groovy深入探索——Metaclass的存放

    本文介紹了Metaclass在Groovy中的存放方式,并對不同的情況(Per-class Metaclass、POGO Per-instance Metaclass和POJO Per-instance Metaclass)進行了分析。

    注:以下分析的Groovy源代碼來自Groovy 1.7.1,所有測試代碼在Groovy 1.7.1下測試通過。

    Metaclass
    用過Groovy的程序員都或多或少、直接或間接的接觸過Metaclass。簡單來說,Metaclass就是Class的Class,Class定義了該類實例的行為,Metaclass則定義了該類及其實例的行為(http://en.wikipedia.org/wiki/Metaclass)。Groovy通過Metaclass使程序可以在運行時修改/添加類的方法、屬性等。

    Per-class Metaclass
    在Groovy中,每個Class都有一個對應的Metaclass,通過這個Metaclass可以給這個Class添加方法或?qū)傩裕?br />
    1 // 給String類添加了一個名為capitalize的方法
    2 String.metaClass.capitalize = { -> delegate[0].toUpperCase() + delegate[1..-1] }
    3 // 給String類添加了一個名為spaceCount的只讀屬性
    4 String.metaClass.getSpaceCount = { -> delegate.count(' ') }
    5 
    6 assert "this is groovy".capitalize() == "This is groovy"
    7 assert "this is not ruby".spaceCount == 3

    除此之外,還可以替換Class對應的Metaclass:
    1 def newMetaClass = new ExpandoMetaClass(Integer)
    2 newMetaClass.initialize()
    3 // 替換Integer類的Metaclass
    4 Integer.metaClass = newMetaClass
    5 assert Integer.metaClass == newMetaClass

    但是,Class對應的Metaclass到底存放在哪里呢?

    我們可以知道,Java的Class類中是沒有存放Metaclass的屬性的,而Groovy中的Class類就是Java中的Class類。那么,Groovy就需要一個全局的Map來存放每個Class對應的Metaclass了,其中每個Entry的key是Class,value則是Metaclass。這個全局的Map不需要對key(Class)進行排序,所以可以使用HashMap;應該通過是否是同一個實例來判斷key的相等性,所以應該是一個IdentityHashMap;不應該妨礙Class被回收,而且Class被回收時對應的Metaclass也應該被回收,所以應該是一個WeakHashMap;可能被多個線程同時使用,所以應該是一個ConcurrentHashMap。總的來說,這個全局的Map應該是一個WeakIdentityConcurrentHashMap。

    在Groovy中,這個全局的Map其實就是groovy.lang.MetaClassRegistry,不過在實現(xiàn)細節(jié)上有所區(qū)別(或者說更加復雜)。MetaClassRegistry是一個interface,它在Groovy中的唯一實現(xiàn)是org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl。我們主要來看MetaClassRegistryImpl中的getMetaClass方法,這個方法用于查找Class對應的Metaclass。
    1 public final MetaClass getMetaClass(Class theClass) {
    2     return ClassInfo.getClassInfo(theClass).getMetaClass();
    3 }

    我們再來看看ClassInfo的getClassInfo靜態(tài)方法:
     1 public static ClassInfo getClassInfo(Class cls) {
     2     // localMapRef的類型是WeakReference<ThreadLocalMapHandler>
     3     ThreadLocalMapHandler handler = localMapRef.get();
     4     SoftReference<LocalMap> ref=null;
     5     if (handler!=null) ref = handler.get();
     6     LocalMap map=null;
     7     if (ref!=null) map = ref.get();
     8     if (map!=nullreturn map.get(cls);
     9     // 只有當localMapRef或ref已被回收時,才會調(diào)用下面的代碼
    10     // globalClassSet的類型是ClassInfoSet
    11     return (ClassInfo) globalClassSet.getOrPut(cls,null);
    12 }

    ClassInfo中使用靜態(tài)的globalClassSet存儲Metaclass,不過并不是直接存儲Class到Metaclass的映射,而是Class到ClassInfo的映射,而ClassInfo則包含了Groovy中跟Class相關的內(nèi)部屬性,其中就包括了Metaclass。

    globalClassSet的類型是ClassInfoSet,而ClassInfoSet類繼承了ManagedConcurrentMap<Class,ClassInfo>類型,而ManagedConcurrentMap其實就是Weak(Soft)IdentityConcurrentHashMap。限于篇幅,我們在這里就不分析ManagedConcurrentMap和ClassInfoSet的代碼了。

    再回到ClassInfo的getClassInfo方法,其中第3到8行實現(xiàn)了一個基于ThreadLocal的兩級cache。我們先來看第5行的“handler.get()”,這里調(diào)用了ThreadLocalMapHandler的get方法:
     1 private static class ThreadLocalMapHandler extends ThreadLocal<SoftReference<LocalMap>> {
     2     SoftReference<LocalMap> recentThreadMapRef; // 最近一次使用的引用
     3     
     4     public SoftReference<LocalMap> get() {
     5         SoftReference<LocalMap> mapRef = recentThreadMapRef;
     6         LocalMap recent = null;
     7         if (mapRef!=null) recent = mapRef.get();
     8         // 如果最近一次使用的引用就是由當前進程創(chuàng)建的,則直接返回該引用,否則才調(diào)用ThreadLocal的get方法。這樣可以減少在ThreadLocal.get()中查找Map的消耗
     9         if (recent != null && recent.myThread.get() == Thread.currentThread()) {
    10             return mapRef;
    11         } else {
    12             SoftReference<LocalMap> ref = super.get();
    13             recentThreadMapRef = ref; // 更新最近一次使用的引用
    14             return ref;
    15         }
    16     }
    17 }

    再來看ClassInfo.getClassInfo(Class)中第8行的“map.get(cls)”,這里調(diào)用了LocalMap的get方法:
     1 private static final class LocalMap extends HashMap<Class,ClassInfo> { // LocalMap本身就是二級cache
     2     private static final int CACHE_SIZE = 5;
     3     
     4     private final ClassInfo[] cache = new ClassInfo[CACHE_SIZE]; // 這是大小為5的一級cache
     5     
     6     public ClassInfo get(Class key) {
     7         ClassInfo info = getFromCache(key); // 先在一級cache中查找
     8         if (info != null)
     9           return info;
    10 
    11         info = super.get(key); // 再在二級cache中查找
    12         if (info != null)
    13           return putToCache(info); // 寫入一級cache
    14 
    15         // 如果在兩級cache中都找不到,則在globalClassSet中查找,再將結(jié)果寫入一級cache
    16         return putToCache((ClassInfo) globalClassSet.getOrPut(key,null));
    17     }
    18     
    19 }

    注意,這里并沒有將任何的結(jié)果寫入到二級cache中,因此二級cache永遠是空的。我認為這可能是被遺漏了,也可能是發(fā)現(xiàn)了二級cache占用了大量內(nèi)存(或者效果并不明顯),所以將寫入二級cache的語句去掉了,但是忘了去掉在二級cache中查找的語句。

    最后,在MetaClassRegistryImpl.getMetaClass(Class)方法中,查找到Class對應的ClassInfo后,再調(diào)用ClassInfo的getMetaClass方法獲得Class對應的Metaclass。但是ClassInfo.getMetaClass()并不是簡單的返回Metaclass,其中還分為對Metaclass的強引用和弱引用兩種情況。由于這已經(jīng)超出了本文討論的范圍,因此不再深入,對此有興趣的讀者可閱讀分析相關代碼。

    POGO Per-instance Metaclass
    POGO的全稱是Plain Old Groovy Object,一般指的就是用Groovy編寫的對象。每個POGO實例都有一個對應的Metaclass,默認情況下該Metaclass與類的Metaclass相同:
    1 class POGO {} // 這是一個用Groovy編寫的對象
    2 
    3 def pogo = new POGO()
    4 assert pogo.metaClass == POGO.metaClass // 默認情況下POGO實例的Metaclass與類的Metaclass相同
    5 
    6 pogo.metaClass.hello = { -> println 'Hello' }
    7 assert pogo.metaClass != POGO.metaClass // 修改POGO實例的Metaclass后,該POGO實例將擁有獨立的Metaclass

    我們通過groovyc編譯上面的腳本,再通過javap反匯編POGO類,最后手工反編譯字節(jié)碼,可以得到以下代碼:
     1 public class POGO implements GroovyObject { // 用Groovy編寫的類都實現(xiàn)了GroovyObject接口
     2     
     3     private transient MetaClass metaClass; // 用于存儲實例對應的Metaclass
     4     
     5     public MetaClass getMetaClass() { // 實現(xiàn)了GroovyObject.getMetaClass()方法
     6         if (metaClass != null)
     7             return metaClass;
     8         metaClass = $getStaticMetaClass(); // 默認情況下返回類的Metaclass
     9         return metaClass;
    10     }
    11 
    12     public void setMetaClass(MetaClass metaClass) { // 實現(xiàn)了GroovyObject.setMetaClass(MetaClass)方法
    13         this.metaClass = metaClass;
    14     }
    15 
    16     protected MetaClass $getStaticMetaClass() {
    17         ClassInfo classinfo = $staticClassInfo;
    18         if (classinfo == null)
    19             $staticClassInfo = classinfo = ClassInfo.getClassInfo(getClass());
    20         return classinfo.getMetaClass();
    21     }
    22     
    23 }

    容易看出,POGO都實現(xiàn)了GroovyObject接口,而該接口中的getMetaClass和setMetaClass方法分別負責實例對應的Metaclass的讀和寫。而POGO實例對應的Metaclass則直接存放在實例中的metaClass字段中。

    POJO Per-instance Metaclass
    與POGO類似的,POJO一般指的就是用Java編寫的對象。自Groovy 1.6開始,可以為每個POJO實例設置不同的Metaclass了:
    1 def s1 = "this is groovy"
    2 def s2 = "this is not ruby"
    3 assert s1.size() == 14
    4 assert s2.size() == 16
    5 s1.metaClass.size = { -> 10 } // 只修改s1的Metaclass
    6 assert s1.size() == 10
    7 assert s2.size() == 16

    與Per-class Metaclass的情況一樣(其實Class也是一個Java類),POJO中并沒有存放Metaclass的屬性,所以需要用一個(或每個類一個)WeakIdentityConcurrentHashMap來存放Object到Metaclass的映射關系。

    POJO Per-instance Metaclass是通過MetaClassRegistryImpl的getMetaClass(Object)和setMetaClass(Object, MetaClass)方法實現(xiàn)讀和寫的,但是這兩個方法并沒有加入到MetaClassRegistry接口中(可能是為了保持兼容性)。我們主要看一下MetaClassRegistryImpl.getMetaClass(Object)方法:
    1 public MetaClass getMetaClass(Object obj) {
    2     return ClassInfo.getClassInfo(obj.getClass()).getMetaClass(obj);
    3 }

    跟getMetaClass(Class)方法一樣,先通過ClassInfo.getClassInfo(Class)方法查找ClassInfo實例。接著調(diào)用了ClassInfo.getMetaClass(Object)方法,我們來看看這個方法:
     1 public MetaClass getMetaClass(Object obj) {
     2     final MetaClass instanceMetaClass = getPerInstanceMetaClass(obj);
     3     if (instanceMetaClass != null)
     4         return instanceMetaClass;
     5 
     6     // 如果沒有為該對象設置Metaclass,則返回類的Metaclass,即默認的Metaclass就是類的Metaclass
     7     lock();
     8     try {
     9         return getMetaClassUnderLock();
    10     } finally {
    11         unlock();
    12     }
    13 }
    14 
    15 public MetaClass getPerInstanceMetaClass(Object obj) {
    16     if (perInstanceMetaClassMap == null)
    17       return null;
    18 
    19     return (MetaClass) perInstanceMetaClassMap.get(obj);
    20 }

    getMetaClass(Object)方法先從perInstanceMetaClassMap屬性中查找obj對應的Metaclass,而perInstanceMetaClassMap屬性的類型是ManagedConcurrentMap,沒錯,就是我們上面提到過的Weak(Soft)IdentityConcurrentHashMap的實現(xiàn)。

    也就是說,POJO對應的Metaclass是存放在它的Class對應的ClassInfo中的一個ManagedConcurrentMap中的。

    總結(jié)
    總的來說,在各種情況下,Metaclass的存放方式如下:
    • Per-class Metaclass:存放在Class對應的ClassInfo中,而Class到ClassInfo的映射關系則存放在ClassInfo中的一個靜態(tài)的ManagedConcurrentMap中;
    • POGO Per-instance Metaclass:直接存放在對象的metaClass字段中。
    • POJO Per-instance Metaclass:對象到Metaclass的映射關系存放在該對象的Class對應的ClassInfo中的一個ManagedConcurrentMap中。
    以上分析有不當之處敬請指出,謝謝大家的閱讀。

    posted on 2010-03-19 20:02 Johnny Jian 閱讀(3652) 評論(0)  編輯  收藏 所屬分類: Groovy

    主站蜘蛛池模板: 国产大陆亚洲精品国产| 中文字幕在线日亚洲9| 国产特黄一级一片免费| 亚洲国产精品碰碰| 日本亚洲高清乱码中文在线观看| 卡1卡2卡3卡4卡5免费视频| jiz zz在亚洲| 国产大片线上免费看| 色九月亚洲综合网| 亚洲AV无码一区二区三区国产| 精品国产日韩亚洲一区在线| 免费萌白酱国产一区二区| 添bbb免费观看高清视频| 国产精品亚洲综合一区| 两个人日本免费完整版在线观看1| 亚洲国产精品无码久久久蜜芽| 亚欧免费无码aⅴ在线观看| 亚洲一区二区三区免费观看| 久久久久国色AV免费观看性色| MM1313亚洲国产精品| 精品国产日韩亚洲一区| 在线看无码的免费网站| 狠狠色香婷婷久久亚洲精品| 国产一级高清免费观看| 99re8这里有精品热视频免费| 亚洲一卡2卡三卡4卡有限公司| 无码区日韩特区永久免费系列| 亚洲国产成人无码AV在线| 亚洲色偷偷综合亚洲AV伊人| 99久久国产免费-99久久国产免费| 亚洲欧洲精品视频在线观看| 日韩毛片无码永久免费看| GOGOGO高清免费看韩国| 亚洲首页国产精品丝袜| 久久精品国产亚洲AV不卡| 亚洲免费网站在线观看| 日本精品久久久久久久久免费| 老汉色老汉首页a亚洲| 国产精品成人四虎免费视频| 四虎影视成人永久免费观看视频 | 最近中文字幕mv免费高清电影 |