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

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

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

    agapple

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      13 Posts :: 1 Stories :: 1 Comments :: 0 Trackbacks

    背景

        前段時間在工作中,包括一些代碼閱讀過程中,spring aop經(jīng)常性的會看到cglib中的相關內(nèi)容,包括BeanCopier,BulkBean,Enancher等內(nèi)容,以前雖大致知道一些內(nèi)容,原理是通過bytecode,但沒具體深入代碼研究,只知其所用不知其所以然,所以就特地花了半天多的工作時間研究了CGLIB的相關源碼,同時結(jié)合看了下 spring Aop中對CGLIB的使用。

    cglib基本信息

    1. cglib的官方網(wǎng)站: http://cglib.sourceforge.net/
    2. cglib目前的最新版本應該是2.2,公司普遍使用的版本也是這個
    3. 官網(wǎng)的samples : http://cglib.sourceforge.net/xref/samples/

    cglib代碼包結(jié)構

    • core (核心代碼)
      • EmitUtils
      • ReflectUtils
      • KeyFactory
      • ClassEmitter/CodeEmitter
      • NamingPolicy/DefaultNamingPolicy
      • GeneratorStrategy/DefaultGeneratorStrategy
      • DebuggingClassWriter
      • ClassGenerator/AbstractClassGenerator
    • beans (bean操作類)
      • BeanCopier
      • BulkBean
      • BeanMap
      • ImmutableBean
      • BeanGenerator
    • reflect
      • FastClass
    • proxy
      • Enhancer
      • CallbackGenerator
      • Callback
        • MethodInterceptor , Dispatcher, LazyLoader , ProxyRefDispatcher , NoOp , FixedValue , InvocationHandler(提供和jdk proxy的功能)
      • CallbackFilter
    • util
      • StringSwitcher 
      • ParallelSorter 
    • transform 

    core核心代碼部分

    EmitUtils

    重要的工具類,主要封裝了一些操作bytecode的基本函數(shù),比如生成一個null_constructor,添加類屬性add_property等

    ReflectUtils

    處理jdk reflect的工具類,比如獲取一個類所有的Method,獲取構造函數(shù)信息等。

    ClassEmitter/CodeEmitter

    對asm的classAdapter和MethodAdapter的實現(xiàn),貫穿于cglib代碼的處理

    KeyFactory

    類庫中重要的唯一標識生成器,用于cglib做cache時做map key,比較底層的基礎類。
    例子:

    interface BulkBeanKey {
    public Object newInstance(String target, String[] getters, String[] setters, String[] types);
    }

    (BulkBeanKey)KeyFactory.create(BulkBeanKey.
    class).newInstance(targetClassName, getters, setters, typeClassNames);



    說明:

    • 每個Key接口,都必須提供newInstance方法,但具體的參數(shù)可以隨意定義,通過newInstance返回的為一個唯一標示,只有當傳入的所有參數(shù)的equals都返回true時,生成的key才是相同的,這就相當于多key的概念。

    NamingPolicy

    默認的實現(xiàn)類:DefaultNamingPolicy, 具體cglib動態(tài)生成類的命名控制。
    一般的命名規(guī)則:

    • 被代理class name + "$$" + 使用cglib處理的class name + "ByCGLIB" + "$$" + key的hashcode
    • 示例:FastSource$$FastClassByCGLIB$$e1a36bab.class

    GeneratorStrategy

    默認的實現(xiàn)類: DefaultGeneratorStrategy
    控制ClassGenerator生成class的byte數(shù)據(jù),中間可插入自己的處理。注意這里依賴了:DebuggingClassWriter進行class generator的處理

    DebuggingClassWriter

    cglib封裝asm的處理類,用于生成class的byte流,通過GeneratorStrategy回調(diào)ClassGenerator.generateClass(DebuggingClassWriter),將自定義的class byte處理回調(diào)給具體的cglib上層操作類,比如由具體的BeanCopier去控制bytecode的生成。

    ClassGenerator

    其中一個抽象實現(xiàn):AbstractClassGenerator。cglib代碼中核心的Class bytecode操作主體,包含了一些cache,調(diào)用NamingPolicy,GeneratorStrategy進行處理,可以說是一個最核心的調(diào)度者。

     

     

    對應的類圖:

     

    1. 外部的BeanCopier都包含了一Generator,繼承自AbstractClassGenerator,實現(xiàn)了generateClass(ClassVisitor v),Object firstInstance(Class type)方法。
    2. AbstractClassGenerator自身會根據(jù)Source進行cache,所以針對已經(jīng)生成過的class,這里KeyFactory對應的值要相等,則會直接返回cache中的結(jié)果。所以BeanCopier每次create慢只是每次都需要new兩個對象,一個是KeyFactory.newInstance,另一個是firstInstance方法調(diào)用生成一個對象。

    反編譯tips

    大家都知道cglib是進行bytecode操作,會動態(tài)生成class,最快最直接的學習就是結(jié)合他生成的class,對照代碼進行學習,效果會好很多。

    system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定輸出目錄"); 

     可參見 cores/DebuggingClassWriter代碼。說明:這樣cglib會將動態(tài)生成的每個class都輸出到文件中,然后我們可以通過decomp進行反編譯查看源碼。

     

    beans (相關操作類)

    BeanCopier

    簡單的示例代碼就不做介紹,相信大家都指導怎么用,這里主要介紹下Convert的使用。

    • 許多網(wǎng)友都做過BeanCopier,BeanUtils的測試,基本BeanCopier的性能是BeanUtils的10倍以上。,出了反射這一性能差異外,BeanUtils默認是開啟Converter功能,允許同名,不同類型的屬性進行拷貝,比如Date對象到String屬性。
    • 有興趣的同學可以去比較下PropertyUtils,默認不開啟Converter功能,發(fā)現(xiàn)性能是BeanUtils的2倍多。

    初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);
    第三個參數(shù)useConverter,是否開啟Convert,默認BeanCopier只會做同名,同類型屬性的copier,否則就會報錯。

    
    
    public class BeanCopierTest {

        
    public static void main(String args[]) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, 
    "/tmp/1");
            BeanCopier copier 
    = BeanCopier.create(Source.class, Target.classtrue);
            Source from 
    = new Source();
            from.setValue(
    1);

            Target to 
    = new Target();
            Converter converter 
    = new BigIntConverter();
            copier.copy(from, to, converter); 
    //使用converter類

            System.out.println(to.getValue());
        }

    }


    class BigIntConverter implements net.sf.cglib.core.Converter {

        @Override
        
    public Object convert(Object value, Class target, Object context) {
            System.out.println(value.getClass() 
    + " " + value); // from類中的value對象
            System.out.println(target); // to類中的定義的參數(shù)對象
            System.out.println(context.getClass() + " " + context); // String對象,具體的方法名
            if (target.isAssignableFrom(BigInteger.class)) {
                
    return new BigInteger(value.toString());
            }
     else {
                
    return value;
            }

        }


    }

    ----
    反編譯后看的代碼:
    public class Target$$BeanCopierByCGLIB$$e1c34377 extends BeanCopier
    {
        
    public void copy(Object obj, Object obj1, Converter converter)
        
    {
            Target target 
    = (Target)obj1;
            Source source 
    = (Source)obj;
            
    // 注意是直接調(diào)用,沒有通過reflect
            target.setValue((BigInteger)converter.convert(new Integer(source.getValue()), CGLIB$load_class$java$2Emath$2EBigInteger, "setValue")); 
        }

    }

    使用注意

    1. 避免每次進行BeanCopier.create創(chuàng)建對象,一般建議是通過static BeanCopier copier = BeanCopier.create()
    2. 合理使用converter。
    3. 應用場景:兩個對象之間同名同屬性的數(shù)據(jù)拷貝, 不能單獨針對其中的幾個屬性單獨拷貝

    BulkBean

         相比于BeanCopier,BulkBean將整個Copy的動作拆分為getPropertyValues,setPropertyValues的兩個方法,允許自定義處理的屬性。

     

    public class BulkBeanTest {

        
    public static void main(String args[]) {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, 
    "/home/ljh/cglib");
            String[] getter 
    = new String[] "getValue" };
            String[] setter 
    = new String[] "setValue" };
            Class[] clazzs 
    = new Class[] int.class };

            BulkBean bean 
    = BulkBean.create(BulkSource.class, getter, setter, clazzs);
            BulkSource obj 
    = new BulkSource();
            obj.setValue(
    1);

            Object[] objs 
    = bean.getPropertyValues(obj);
            
    for (Object tmp : objs) {
                System.out.println(tmp);
            }

        }

    }

    class BulkSource {
        
    private int value;
        ..
    }


    // 反編譯后的代碼: 
     public void getPropertyValues(Object obj, Object aobj[])
        
    {
            BulkSource bulksource 
    = (BulkSource)obj;
            aobj[
    0= new Integer(bulksource.getValue());
        }

     

     

     

    使用注意

    1. 避免每次進行BulkBean.create創(chuàng)建對象,一般建議是通過static BulkBean.create copier = BulkBean.create
    2. 應用場景:針對特定屬性的get,set操作,一般適用通過xml配置注入和注出的屬性,運行時才確定處理的Source,Target類,只需關注屬性名即可。

    BeanMap

    相比于BeanCopier,BulkBean,都是針對兩個Pojo Bean進行處理,那如果對象一個是Pojo Bean和Map對象之間,那就得看看BeanMap,將一個java bean允許通過map的api進行調(diào)用。
    幾個支持的操作接口:

    • Object get(Object key)
    • Object put(Object key, Object value)
    • void putAll(Map t)
    • Set entrySet()
    • Collection values()
    • boolean containsKey(Object key)
    • ....
    public class BeanMapTest {

        
    public static void main(String args[]) {
            
    // 初始化
            BeanMap map = BeanMap.create(new Pojo());
            
    // 構造
            Pojo pojo = new Pojo();
            pojo.setIntValue(
    1);
            pojo.setBigInteger(
    new BigInteger("2"));
            
    // 賦值
            map.setBean(pojo);
            
    // 驗證
            System.out.println(map.get("intValue"));
            System.out.println(map.keySet());
            System.out.println(map.values());
        }

    }


    class Pojo {

        
    private int        intValue;
        
    private BigInteger bigInteger;
        .
    }


    //反編譯代碼查看:
    //首先保存了所有的屬性到一個set中
    private static FixedKeySet keys = new FixedKeySet(new String[] {
            
    "bigInteger""intValue"
        }
    );
    public Object get(Object obj, Object obj1)
        
    {
            (Pojo)obj;
            String s 
    = (String)obj1;
            s;
            s.hashCode();
            JVM INSTR lookupswitch 
    2default 72
        
    //                   -139068386: 40
        
    //                   556050114: 52;
               goto _L1 _L2 _L3
    _L2:
            
    "bigInteger";
     
    //屬性判斷是否相等
            equals();
            JVM INSTR ifeq 
    73;
               
    goto _L4 _L5
    _L5:
            
    break MISSING_BLOCK_LABEL_73;
    _L4:
            getBigInteger();
            
    return;
    _L3:

    .

    }

    使用注意

    1. 避免每次進行BeanMap map = BeanMap.create();創(chuàng)建對象,不同于BeanCopier對象,BeanMap主要針對對象實例進行處理,所以一般建議是map.setBean(pojo);進行動態(tài)替換持有的對象實例。
    2. 應用場景:針對put,putAll操作會直接修改pojo對象里的屬性,所以可以通過beanMap.putAll(map)進行map<->pojo屬性的拷貝。

     

    BeanGenerator

       暫時沒有想到合適的使用場景,不過BeanGenerator使用概念是很簡單的,就是將一個Map<String,Class>properties的屬性定義,動態(tài)生成一個pojo bean類。

     

    BeanGenerator generator = new BeanGenerator();
    generator.addProperty(
    "intValue"int.class);
    generator.addProperty(
    "integer", Integer.class);
    generator.addProperty(
    "properties", Properties.class);
           
    Class clazz 
    = (Class) generator.createClass();
    Object obj 
    = generator.create();

    PropertyDescriptor[] getters 
    = ReflectUtils.getBeanGetters(obj.getClass());
    for (PropertyDescriptor getter : getters) {
        Method write 
    = getter.getWriteMethod();
        System.out.println(write.getName());
    }

     

    ImmutableBean

    bean Immutable模式的一種動態(tài)class實現(xiàn),Immutable模式主要應用于服務設計上,返回的pojo bean對象,不運行進行write方法調(diào)用

    說明

    個人是不太建議使用cglib動態(tài)class的方式來實現(xiàn)bean Immutable的模式,Immutable模式應該是一種服務接口上的顯示聲明,而不是如此隱晦,而且pojo bean盡量做到是輕量級,簡答的set/get方法,如果要做充血的領域模型那就另當別論了。

     

    reflect (class,method處理)

    FastClass

    顧明思義,F(xiàn)astClass就是對Class對象進行特定的處理,比如通過數(shù)組保存method引用,因此FastClass引出了一個index下標的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的獲取method的方法。
    通過數(shù)組存儲method,constructor等class信息,從而將原先的反射調(diào)用,轉(zhuǎn)化為class.index的直接調(diào)用,從而體現(xiàn)所謂的FastClass。

    public class FastClassTest {
        
    public static void main(String args[]) throws Exception {
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, 
    "/home/ljh/cglib");

            FastClass clazz 
    = FastClass.create(FastSource.class);
            
    // fast class反射調(diào)用
            FastSource obj = (FastSource) clazz.newInstance();
            clazz.invoke(
    "setValue"new Class[] int.class }, obj, new Object[] 1 });
            clazz.invoke(
    "setOther"new Class[] int.class }, obj, new Object[] 2 });

            
    int value = (Integer) clazz.invoke("getValue"new Class[] {}, obj, new Object[] {});
            
    int other = (Integer) clazz.invoke("getOther"new Class[] {}, obj, new Object[] {});
            System.out.println(value 
    + " " + other);
            
    // fastMethod使用
            FastMethod setValue = clazz.getMethod("setValue"new Class[] int.class });
            System.out.println(
    "setValue index is : " + setValue.getIndex());

            FastMethod getValue 
    = clazz.getMethod("getValue"new Class[] {});
            System.out.println(
    "getValue index is : " + getValue.getIndex());

            FastMethod setOther 
    = clazz.getMethod("setOther"new Class[] int.class });
            System.out.println(
    "setOther index is : " + setOther.getIndex());

            FastMethod getOther 
    = clazz.getMethod("getOther"new Class[] {});
            System.out.println(
    "getOther index is : " + getOther.getIndex());
            
    // 其他
            System.out.println("getDeclaredMethods : " + clazz.getJavaClass().getDeclaredMethods().length);
            System.out.println(
    "getConstructors : " + clazz.getJavaClass().getConstructors().length);
            System.out.println(
    "getFields : " + clazz.getJavaClass().getFields().length);
            System.out.println(
    "getMaxIndex : " + clazz.getMaxIndex());
        }

    }


    class FastSource {
        
    private int value;
        
    private int other;

    }

    proxy (spring aop相關)

    總體類結(jié)構圖:

     

    Callback & CallbackGenerator

    1. MethodInterceptor
      • 類似于spring aop的around Advise的功能,大家都知道,不多做介紹。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的區(qū)別。invokeSuper是退出當前interceptor的處理,進入下一個callback處理,invoke則會繼續(xù)回調(diào)該方法,如果傳遞給invoke的obj參數(shù)出錯容易造成遞歸調(diào)用
    2. Dispatcher, ProxyRefDispatcher
      • 類似于delegate的模式,直接將請求分發(fā)給具體的Dispatcher調(diào)用,是否有著接口+實現(xiàn)分離的味道,將接口的方法調(diào)用通過Dispatcher轉(zhuǎn)到實現(xiàn)target上。ProxyRefDispatcher與Dispatcher想比,loadObject()多了個當前代理對象的引用。
      •         
        //反編譯的部分代碼
        public final int cal(int i, int j)
        {
                CGLIB$CALLBACK_1;
                
        if(CGLIB$CALLBACK_1 != nullgoto _L2; else goto _L1
        _L1:
                JVM INSTR pop ;
                CGLIB$BIND_CALLBACKS(
        this);
                CGLIB$CALLBACK_1;
        _L2:
                loadObject(); 
        //每次都進行調(diào)用
                (DefaultCalcService);
                i;
                j;
                cal(); 
        //調(diào)用實現(xiàn)類的方法
                return;
            }
         
         
    3. LazyLoader
      • 相比于Dispatcher,lazyLoader在第一次獲取了loadObject后,會進行緩存,后續(xù)的請求調(diào)用都會直接調(diào)用該緩存的屬性.
      •         
        //反編譯部分代碼
        public final int cal(int i, int j)
        {
            
        this;
            
        return ((DefaultCalcService)CGLIB$LOAD_PRIVATE_3()).cal(i, j);
        }


        private final synchronized Object CGLIB$LOAD_PRIVATE_3()
        {
                CGLIB$LAZY_LOADER_3; 
        //保存的屬性
                if(CGLIB$LAZY_LOADER_3 != nullgoto _L2; else goto _L1
        _L1:
                JVM INSTR pop ;
                
        this;
                CGLIB$CALLBACK_3;
                
        if(CGLIB$CALLBACK_3 != nullgoto _L4; else goto _L3
        _L3:
                JVM INSTR pop ;
                CGLIB$BIND_CALLBACKS(
        this);
                CGLIB$CALLBACK_3;
        _L4:
                loadObject();
                JVM INSTR dup_x1 ;
                CGLIB$LAZY_LOADER_3;
        _L2:
                
        return;
            }
    4. NoOp
      • 不做任何處理,結(jié)合Filter針對不需要做代理方法直接返回,調(diào)用其原始方法
    5. FixedValue
      • 強制方法返回固定值,可結(jié)合Filter進行控制
    6. InvocationHandler(提供和jdk proxy的功能),不常用

    CallbackFilter

    主要的作用就是callback調(diào)度,主要的一個方法:int accept(Method method);
    返回的int在int值,代表對應method需要插入的callback,會靜態(tài)生成到class的代碼中,這樣是cglib proxy區(qū)別于jdk proxy的方式,一個是靜態(tài)的代碼調(diào)用,一個是動態(tài)的reflect。
    可以查看: Enhancer類中的emitMethods方法,line:883。在構造class method字節(jié)嗎之前就已經(jīng)確定需要運行的callback。

     

    Enhancer

    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib");
    LogInteceptor logInteceptor 
    = new LogInteceptor();
    CalDispatcher calDispatcher 
    = new CalDispatcher();
    CalcProxyRefDispatcher calcProxyRefDispatcher 
    = new CalcProxyRefDispatcher();
    LazyLoaderCallback lazyLoaderCallback 
    = new LazyLoaderCallback();

    Enhancer enhancer 
    = new Enhancer();
    enhancer.setSuperclass(CalcService.
    class); //接口類
    enhancer.setCallbacks(new Callback[] { logInteceptor, calDispatcher, calcProxyRefDispatcher,lazyLoaderCallback, NoOp.INSTANCE }); // callback數(shù)組
    enhancer.setCallbackFilter(new CalcCallbackFilter()); // filter
    CalcService service = (CalcService) enhancer.create();

    int result = service.cal(11);

    Util  (工具類,感覺有點雞肋)

    • StringSwitcher 提供string和int的map映射查詢,給定一個string字符串,返回同個下標數(shù)組的int值,感覺很雞肋,用Map不是可以很快速的實現(xiàn)功能
    • ParallelSorter 看了具體的代碼,沒啥意思,就是提供了一個二分的快速排序和多路歸并排序。沒有所謂的并行排序,原本以為會涉及多線程處理,可惜沒有

    transform

         暫時沒仔細研究,更多的是對asm的封裝,等下次看了asm代碼后再回來研究下。



    Blog : http://agapple.javaeye.com/  歡迎訪問
    posted on 2010-11-01 22:24 agapple 閱讀(1316) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導航:
     
    主站蜘蛛池模板: 24小时免费直播在线观看| 91亚洲自偷在线观看国产馆| 国产精品69白浆在线观看免费 | 午夜不卡久久精品无码免费 | 成年女人色毛片免费看| 美女视频黄的免费视频网页| 麻豆一区二区三区蜜桃免费| 亚洲精品自偷自拍无码| 亚洲三级视频在线观看| 亚洲国产日韩一区高清在线| 国产亚洲精品看片在线观看| 亚洲JIZZJIZZ中国少妇中文| 在线观看国产情趣免费视频 | 亚洲成人福利网站| 久久精品国产亚洲夜色AV网站 | 精品免费视在线观看| 一级一级一片免费高清| 免费人成大片在线观看播放| 亚洲国产欧美国产综合一区| 亚洲无mate20pro麻豆| 亚洲精品日韩专区silk| 亚洲人成电影福利在线播放 | a毛片免费播放全部完整| 一区在线免费观看| 日本中文字幕免费看| 菠萝菠萝蜜在线免费视频| 国产精品亚洲一区二区三区久久 | 国产一卡二卡四卡免费| 人妻无码一区二区三区免费 | 亚洲va无码手机在线电影| 国产亚洲精品国产| 亚洲国产成人高清在线观看| 亚洲国产精品VA在线观看麻豆| 亚洲国产精品无码专区影院 | 午夜影院免费观看| 2019中文字幕免费电影在线播放| 在线人成精品免费视频| 久久成人国产精品免费软件| 成人免费的性色视频| 成人免费在线观看网站| 在线永久免费观看黄网站|