CGLIB

1、CGLIB    官网:http://cglib.sourceforge.net

               CGLIB是一个强大的高性能的代码生成包。它被许多AOP的框架(例如Spring AOP)使用,为他们提供方法的interception(拦截)。

                 Hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联。EasyMock通过使用模仿(moke)对象来测试java代码的包。

                  它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,

                   来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

                    在使用CGLIB时需引入cglib-nodep-2.x.jar包

        

    CGLIB代码包结构:

          1、core(核心代码)

                  EmitUtils: 重要的工具类,主要封装了一些操作bytecode的基本函数,比如生成一个null_constructor,添加类属性add_property等。

                  ReflectUtils:处理JDK reflect 的工具类,比如获取一个类所有的method,获取构造函数信息等。

                  ClassEmitter  /  CodeEmitter  : 对asm的classAdapter和MethodAdapter的实现,贯穿于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方法,但具体的参数可以随意定义,通过newInstance返回的为一个唯一标示,只有当传入的所有参数的equals都返回true时,生成的key才是相同的,这就相当于多key的概念。

      

                 NamingPolicy: 默认的实现类为DefaultNamingPolicy,具体cglib动态生成类的命名控制。

                              一般的命名规则:

                                          被代理class name+"$$"+使用cglib处理的class name +"ByCGLIB"+“$$”+key的hashcode --> (FastSource$$FastClassByCGLIB$$ea36bab.class)

                 GeneratorStrategy:  默认的实现类:DefaultGeneratorStrategy 控制ClassGenerrator生成class的byte数据,中间可插入自己的处理。

                                                   注意这里依赖于:DebuggingClassWriter进行classgenerator的处理。

                 DebuggingClassWriter: cglib封装asm的处理类,用于生成class的byte流,通过GeneratorStrategy回调ClassGenerator.genrateClass,将自定义的class byte处理

                                                       回调给具体的cglib 上层操作类,比如由具体的BeanCopier去控制bytecode的生成。

                  ClassGenerator: 其中一个抽象类的实现:AbstarctClassGenerator。cglib代码中核心的Class bytecode操作主体,包含了一些cache,

                                      调用NamingPolicy,GeneratorStrategy进行处理,可以说是一个最核心的调度者。

   

大家都知道cglib是进行bytecode操作,会动态生成class,最快最直接的学习就是结合他生成的class,对照代码进行学习,效果会好很多。
 

Java代码  
1.system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定输出目录");   
 
 可参见 cores/DebuggingClassWriter代码。说明:这样cglib会将动态生成的每个class都输出到文件中,然后我们可以通过decomp进行反编译查看源码。

 

             2、beans(相关操作类)

                        BeanCopier :   BeanCopier性能是BenUtils的10倍以上。除了反射这一性能差异外,BeanUtils默认是开启Converter功能且允许同名与

                                                不同类型的属性进行拷贝,如Date对象到String属性。

1.public class BeanCopierTest {  
2.  
3.    public static void main(String args[]) {  
4.        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/1");  
5.        BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);  
6.        Source from = new Source();  
7.        from.setValue(1);  
8.  
9.        Target to = new Target();  
10.        Converter converter = new BigIntConverter();  
11.        copier.copy(from, to, converter); //使用converter类  
12.  
13.        System.out.println(to.getValue());  
14.    }  
15.}  
16.  
17.class BigIntConverter implements net.sf.cglib.core.Converter {  
18.  
19.    @Override  
20.    public Object convert(Object value, Class target, Object context) {  
21.        System.out.println(value.getClass() + " " + value); // from类中的value对象  
22.        System.out.println(target); // to类中的定义的参数对象  
23.        System.out.println(context.getClass() + " " + context); // String对象,具体的方法名  
24.        if (target.isAssignableFrom(BigInteger.class)) {  
25.            return new BigInteger(value.toString());  
26.        } else {  
27.            return value;  
28.        }  
29.    }  
30.  
31.}  
32.----  
33.反编译后看的代码:  
34.public class Target$$BeanCopierByCGLIB$$e1c34377 extends BeanCopier  
35.{  
36.    public void copy(Object obj, Object obj1, Converter converter)  
37.    {  
38.        Target target = (Target)obj1;  
39.        Source source = (Source)obj;  
40.        // 注意是直接调用,没有通过reflect  
41.        target.setValue((BigInteger)converter.convert(new Integer(source.getValue()), CGLIB$load_class$java$2Emath$2EBigInteger, "setValue"));   
42.    }  
43.}  


 

初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true); 
第三个参数useConverter,是否开启Convert,默认BeanCopier只会做同名,同类型属性的copier,否则就会报错。

使用注意

  1. 避免每次进行BeanCopier.create创建对象,一般建议是通过static BeanCopier copier = BeanCopier.create()
  2. 合理使用converter。
  3. 应用场景:两个对象之间同名同属性的数据拷贝, 不能单独针对其中的几个属性单独拷贝

   

                       BulkBean : 相比于BeanCopier,BulkBean将整个Copy的动作拆分为getPropertyValues,setPropertyValues的两个方法,允许自定义处理的属性。

 

1.public class BulkBeanTest {  
2.  
3.    public static void main(String args[]) {  
4.        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib");  
5.        String[] getter = new String[] { "getValue" };  
6.        String[] setter = new String[] { "setValue" };  
7.        Class[] clazzs = new Class[] { int.class };  
8.  
9.        BulkBean bean = BulkBean.create(BulkSource.class, getter, setter, clazzs);  
10.        BulkSource obj = new BulkSource();  
11.        obj.setValue(1);  
12.  
13.        Object[] objs = bean.getPropertyValues(obj);  
14.        for (Object tmp : objs) {  
15.            System.out.println(tmp);  
16.        }  
17.    }  
18.}  
19.class BulkSource {  
20.    private int value;  
21.    .....  
22.}  
23.  
24.// 反编译后的代码:   
25. public void getPropertyValues(Object obj, Object aobj[])  
26.    {  
27.        BulkSource bulksource = (BulkSource)obj;  
28.        aobj[0] = new Integer(bulksource.getValue());  
29.    }  

 

使用注意
 1.避免每次进行BulkBean.create创建对象,一般建议是通过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进行调用。

  

1.public class BeanMapTest {  
2.  
3.    public static void main(String args[]) {  
4.        // 初始化  
5.        BeanMap map = BeanMap.create(new Pojo());  
6.        // 构造  
7.        Pojo pojo = new Pojo();  
8.        pojo.setIntValue(1);  
9.        pojo.setBigInteger(new BigInteger("2"));  
10.        // 赋值  
11.        map.setBean(pojo);  
12.        // 验证  
13.        System.out.println(map.get("intValue"));  
14.        System.out.println(map.keySet());  
15.        System.out.println(map.values());  
16.    }  
17.}  
18.  
19.class Pojo {  
20.  
21.    private int        intValue;  
22.    private BigInteger bigInteger;  
23.    ....  
24.}  
25.  
26.//反编译代码查看:  
27.//首先保存了所有的属性到一个set中  
28.private static FixedKeySet keys = new FixedKeySet(new String[] {  
29.        "bigInteger", "intValue"  
30.    });  
31.public Object get(Object obj, Object obj1)  
32.    {  
33.        (Pojo)obj;  
34.        String s = (String)obj1;  
35.        s;  
36.        s.hashCode();  
37.        JVM INSTR lookupswitch 2: default 72  
38.    //                   -139068386: 40  
39.    //                   556050114: 52;  
40.           goto _L1 _L2 _L3  
41._L2:  
42.        "bigInteger";  
43. //属性判断是否相等  
44.        equals();  
45.        JVM INSTR ifeq 73;  
46.           goto _L4 _L5  
47._L5:  
48.        break MISSING_BLOCK_LABEL_73;  
49._L4:  
50.        getBigInteger();  
51.        return;  
52._L3:  
53.  
54.....  
55.  
56.}  

使用注意

  1. 避免每次进行BeanMap map = BeanMap.create();创建对象,不同于BeanCopier对象,BeanMap主要针对对象实例进行处理,所以一般建议是map.setBean(pojo);进行动态替换持有的对象实例。
  2. 应用场景:针对put,putAll操作会直接修改pojo对象里的属性,所以可以通过beanMap.putAll(map)进行map<->pojo属性的拷贝。

 

                 BeanGenerator : 就是将一个Map<String,Class>properties的属性定义,动态生成一个pojo bean类。

   

1.BeanGenerator generator = new BeanGenerator();  
2.generator.addProperty("intValue", int.class);  
3.generator.addProperty("integer", Integer.class);  
4.generator.addProperty("properties", Properties.class);  
5.         
6.Class clazz = (Class) generator.createClass();  
7.Object obj = generator.create();  
8.  
9.PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(obj.getClass());  
10.for (PropertyDescriptor getter : getters) {  
11.    Method write = getter.getWriteMethod();  
12.    System.out.println(write.getName());  
13.}


                  ImmutableBean:  bean Immutable模式的一种动态class实现,Immutable模式主要应用与服务设计上,返回的pojo bean对象,不运行进行writer方法调用。

            

        3、FastClass

                     就是对class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,

                     比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。

                      通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的FastClass。

1.public class FastClassTest {  
2.    public static void main(String args[]) throws Exception {  
3.        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/home/ljh/cglib");  
4.  
5.        FastClass clazz = FastClass.create(FastSource.class);  
6.        // fast class反射调用  
7.        FastSource obj = (FastSource) clazz.newInstance();  
8.        clazz.invoke("setValue", new Class[] { int.class }, obj, new Object[] { 1 });  
9.        clazz.invoke("setOther", new Class[] { int.class }, obj, new Object[] { 2 });  
10.  
11.        int value = (Integer) clazz.invoke("getValue", new Class[] {}, obj, new Object[] {});  
12.        int other = (Integer) clazz.invoke("getOther", new Class[] {}, obj, new Object[] {});  
13.        System.out.println(value + " " + other);  
14.        // fastMethod使用  
15.        FastMethod setValue = clazz.getMethod("setValue", new Class[] { int.class });  
16.        System.out.println("setValue index is : " + setValue.getIndex());  
17.  
18.        FastMethod getValue = clazz.getMethod("getValue", new Class[] {});  
19.        System.out.println("getValue index is : " + getValue.getIndex());  
20.  
21.        FastMethod setOther = clazz.getMethod("setOther", new Class[] { int.class });  
22.        System.out.println("setOther index is : " + setOther.getIndex());  
23.  
24.        FastMethod getOther = clazz.getMethod("getOther", new Class[] {});  
25.        System.out.println("getOther index is : " + getOther.getIndex());  
26.        // 其他  
27.        System.out.println("getDeclaredMethods : " + clazz.getJavaClass().getDeclaredMethods().length);  
28.        System.out.println("getConstructors : " + clazz.getJavaClass().getConstructors().length);  
29.        System.out.println("getFields : " + clazz.getJavaClass().getFields().length);  
30.        System.out.println("getMaxIndex : " + clazz.getMaxIndex());  
31.    }  
32.}  
33.  
34.class FastSource {  
35.    private int value;  
36.    private int other;  
37.  
38.}  

 

        4、proxy

  CGLIB_第1张图片

          CallBack  &   CallbackGenerator

                         MethodInterceptor:

                                 类似spring aop 的around Advise的功能。其中需要注意的是:proxy.invokeSuper是退出当前interceptor的处理,进入到下一个callback处理;

                                   proxy.invoke则会继续回调该方法,如果传递给invoke的obj参数出错容易造成递归调用。

                        Dispatcher、ProxyRefDispatcher

                                 类似于delegate的模式,直接将请求发送给具体的Dispatcher调用,将接口的方法调用通过Dispatcher转到实现的target上。

                                 ProxyRefDispatcher与Dispatcher相比,loadObject()多了个当前代理对象的引用。

                        LazyLoader

                             相比Dispatcher,lazyLoader在第一次获取了loadObject之后,会进行缓存,或许的请求都会直接调用该缓存的属性。

                         NoOp

                                不做任何处理,结合Filter针对不需要做代理方法直接返回,调用其原始方法。

                         FixedValue

                                强制方法返回固定值,可结合FIlter进行控制。

                          InvocationHander(和jdk proxy功能)

              CallbackFilter

                       主要的作用就是callback调度,主要的一个方法:int accept(Method method);返回int值,代表对应的method需要插入的callback,

                        会静态生成到class的代码中,这样是cglib proxy 区别于 jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect.

               Enhancer

 

       5、Util(工具类)

                   StringSwitcher

                   ParallelSorter

       6、transform

    

 

 

 

 

 

 

 

 

 

 

 


使用注意:
    1、避免每次进行BeanCopier.create创建对象,一般建议是通过 static BeanCopier copier = BeanCopier.create();
      2、合理使用converter.
      3、应用场景:两个对象之间同名同属性的数据拷贝,不能单独针对其中的几个属性单独拷贝。


      

                   

                       

 

 

 

 

 

 

 

 

 


 

你可能感兴趣的:(cglib)