`
agapple
  • 浏览: 1595535 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

cglib源码学习交流

    博客分类:
  • java
阅读更多

相关性能测试和使用例子,可以参见另一篇文章: cglib相关性能测试对比

 背景

    前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCopier,BulkBean,Enancher等内容,以前虽大致知道一些内容,原理是通过bytecode,但没具体深入代码研究,只知其所用不知其所以然,所以就特地花了半天多的工作时间研究了CGLIB的相关源码,同时结合看了下 spring Aop中对CGLIB的使用。

    本文主要通过对cglib有原理的分析,反编译查看源码,例子等方式做一个介绍。

cglib基本信息

  1. cglib的官方网站: http://cglib.sourceforge.net/
  2. cglib目前的最新版本应该是2.2,公司普遍使用的版本也是这个
  3. 官网的samples : http://cglib.sourceforge.net/xref/samples/

cglib代码包结构

  • 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的基本函数,比如生成一个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$$e1a36bab.class

GeneratorStrategy

默认的实现类: DefaultGeneratorStrategy
控制ClassGenerator生成class的byte数据,中间可插入自己的处理。注意这里依赖了:DebuggingClassWriter进行class generator的处理

DebuggingClassWriter

cglib封装asm的处理类,用于生成class的byte流,通过GeneratorStrategy回调ClassGenerator.generateClass(DebuggingClassWriter),将自定义的class byte处理回调给具体的cglib上层操作类,比如由具体的BeanCopier去控制bytecode的生成。

ClassGenerator

其中一个抽象实现:AbstractClassGenerator。cglib代码中核心的Class bytecode操作主体,包含了一些cache,调用NamingPolicy,GeneratorStrategy进行处理,可以说是一个最核心的调度者。

 

 

对应的类图:

 

  1. 外部的BeanCopier都包含了一Generator,继承自AbstractClassGenerator,实现了generateClass(ClassVisitor v),Object firstInstance(Class type)方法。
  2. AbstractClassGenerator自身会根据Source进行cache,所以针对已经生成过的class,这里KeyFactory对应的值要相等,则会直接返回cache中的结果。所以BeanCopier每次create慢只是每次都需要new两个对象,一个是KeyFactory.newInstance,另一个是firstInstance方法调用生成一个对象。

反编译tips

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

system.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定输出目录"); 

 可参见 cores/DebuggingClassWriter代码。说明:这样cglib会将动态生成的每个class都输出到文件中,然后我们可以通过decomp进行反编译查看源码。

 

beans (相关操作类)

BeanCopier

简单的示例代码就不做介绍,相信大家都指导怎么用,这里主要介绍下Convert的使用。

  • 许多网友都做过BeanCopier,BeanUtils的测试,基本BeanCopier的性能是BeanUtils的10倍以上。,出了反射这一性能差异外,BeanUtils默认是开启Converter功能,允许同名,不同类型的属性进行拷贝,比如Date对象到String属性。
  • 有兴趣的同学可以去比较下PropertyUtils,默认不开启Converter功能,发现性能是BeanUtils的2倍多。

初始化例子:BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);
第三个参数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.class, true);
        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类中的定义的参数对象
        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;
        // 注意是直接调用,没有通过reflect
        target.setValue((BigInteger)converter.convert(new Integer(source.getValue()), CGLIB$load_class$java$2Emath$2EBigInteger, "setValue")); 
    }
}

 

使用注意

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

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创建对象,一般建议是通过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进行调用。
几个支持的操作接口:

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

 

BeanGenerator

   暂时没有想到合适的使用场景,不过BeanGenerator使用概念是很简单的,就是将一个Map<String,Class>properties的属性定义,动态生成一个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模式的一种动态class实现,Immutable模式主要应用于服务设计上,返回的pojo bean对象,不运行进行write方法调用。

 

 

说明

个人是不太建议使用cglib动态class的方式来实现bean Immutable的模式,Immutable模式应该是一种服务接口上的显示声明,而不是如此隐晦,而且pojo bean尽量做到是轻量级,简答的set/get方法,如果要做充血的领域模型那就另当别论了。

 

reflect (class,method处理)

FastClass

顾明思义,FastClass就是对Class对象进行特定的处理,比如通过数组保存method引用,因此FastClass引出了一个index下标的新概念,比如getIndex(String name, Class[] parameterTypes)就是以前的获取method的方法。
通过数组存储method,constructor等class信息,从而将原先的反射调用,转化为class.index的直接调用,从而体现所谓的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反射调用
        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相关)

总体类结构图:

Callback & CallbackGenerator

  1. MethodInterceptor
    • 类似于spring aop的around Advise的功能,大家都知道,不多做介绍。唯一需要注意的就是proxy.invokeSuper和proxy.invoke的区别。invokeSuper是退出当前interceptor的处理,进入下一个callback处理,invoke则会继续回调该方法,如果传递给invoke的obj参数出错容易造成递归调用
  2. Dispatcher, ProxyRefDispatcher
    • 类似于delegate的模式,直接将请求分发给具体的Dispatcher调用,是否有着接口+实现分离的味道,将接口的方法调用通过Dispatcher转到实现target上。ProxyRefDispatcher与Dispatcher想比,loadObject()多了个当前代理对象的引用。
    • //反编译的部分代码
      public final int cal(int i, int j)
      {
              CGLIB$CALLBACK_1;
              if(CGLIB$CALLBACK_1 != null) goto _L2; else goto _L1
      _L1:
              JVM INSTR pop ;
              CGLIB$BIND_CALLBACKS(this);
              CGLIB$CALLBACK_1;
      _L2:
              loadObject(); //每次都进行调用
              (DefaultCalcService);
              i;
              j;
              cal(); //调用实现类的方法
              return;
          } 
  3. LazyLoader
    • 相比于Dispatcher,lazyLoader在第一次获取了loadObject后,会进行缓存,后续的请求调用都会直接调用该缓存的属性.
    • //反编译部分代码
      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 != null) goto _L2; else goto _L1
      _L1:
              JVM INSTR pop ;
              this;
              CGLIB$CALLBACK_3;
              if(CGLIB$CALLBACK_3 != null) goto _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
    • 不做任何处理,结合Filter针对不需要做代理方法直接返回,调用其原始方法
  5. FixedValue
    • 强制方法返回固定值,可结合Filter进行控制
  6. InvocationHandler(提供和jdk proxy的功能),不常用

CallbackFilter

主要的作用就是callback调度,主要的一个方法:int accept(Method method);
返回的int在int值,代表对应method需要插入的callback,会静态生成到class的代码中,这样是cglib proxy区别于jdk proxy的方式,一个是静态的代码调用,一个是动态的reflect。
可以查看: Enhancer类中的emitMethods方法,line:883。在构造class method字节吗之前就已经确定需要运行的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数组
enhancer.setCallbackFilter(new CalcCallbackFilter()); // filter
CalcService service = (CalcService) enhancer.create();

int result = service.cal(1, 1);

 

Util  (工具类,感觉有点鸡肋)

  • StringSwitcher 提供string和int的map映射查询,给定一个string字符串,返回同个下标数组的int值,感觉很鸡肋,用Map不是可以很快速的实现功能
  • ParallelSorter 看了具体的代码,没啥意思,就是提供了一个二分的快速排序和多路归并排序。没有所谓的并行排序,原本以为会涉及多线程处理,可惜没有

 

transform

     暂时没仔细研究,更多的是对asm的封装,等下次看了asm代码后再回来研究下。

 

 

----- 

 

相关性能测试和使用说明,可以参见另一篇文章:  cglib相关性能测试对比

  • 大小: 13.8 KB
  • 大小: 28.3 KB
分享到:
评论
14 楼 landy8530 2013-03-10  
agapple 写道
你拿到的class不是BeanCopier的class.

这是我的
public class com.agapple.cglib.beans.Target$$BeanCopierByCGLIB$$e1c34377 extends net.sf.cglib.beans.BeanCopier
  SourceFile: "<generated>"
  minor version: 0
  major version: 46
  Constant pool:
const #1 = Asciz	com/agapple/cglib/beans/Target$$BeanCopierByCGLIB$$e1c34377;



而你给的类名是: 
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory


你是不是反编译错了另外一个文件?

你好,可以打包你的测试代码我看一下吗?谢谢@
13 楼 agapple 2013-03-08  
你拿到的class不是BeanCopier的class.

这是我的
public class com.agapple.cglib.beans.Target$$BeanCopierByCGLIB$$e1c34377 extends net.sf.cglib.beans.BeanCopier
  SourceFile: "<generated>"
  minor version: 0
  major version: 46
  Constant pool:
const #1 = Asciz	com/agapple/cglib/beans/Target$$BeanCopierByCGLIB$$e1c34377;



而你给的类名是: 
public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory


你是不是反编译错了另外一个文件?
12 楼 landy8530 2013-03-08  
你好,我按照你上面的做法写了个例子,发现跟你的结果有点不一样。代码如下:
package org.lyx.beancopier;


import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter;
import net.sf.cglib.core.DebuggingClassWriter;

public class BeanCopierTest {
	public static void main(String[] args) {
		 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "d:\\temp");  
	     BeanCopier copier = BeanCopier.create(Source.class, Target.class, true);  
	     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());  
	}
}
package org.lyx.beancopier;
public class Source {
	private int value;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
	
}
package org.lyx.beancopier;
public class Target {
	private int value;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}
}
package org.lyx.beancopier;

import java.math.BigInteger;

import net.sf.cglib.core.Converter;

public class BigIntConverter implements Converter{

	public Object convert(Object value, Class target, Object context) {
		System.out.println(value.getClass() + " " + value); // from类中的value对象  
        System.out.println(target); // to类中的定义的参数对象  
        System.out.println(context.getClass() + " " + context); // String对象,具体的方法名  
        if (target.isAssignableFrom(BigInteger.class)) {  
            return new BigInteger(value.toString());  
        } else {  
            return value;  
        }  
	}

}

这样输出的结果是:
CGLIB debugging enabled, writing to 'd:\temp'
class java.lang.Integer 1
int
class java.lang.String setValue
1
结果是对的。但是反编译出来的那个class文件好像,不是你上面的那样。没有copy方法呢。如下:
package net.sf.cglib.beans;

import net.sf.cglib.core.KeyFactory;

public class BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd extends KeyFactory
  implements BeanCopier.BeanCopierKey
{
  private final String FIELD_0;
  private final String FIELD_1;
  private final boolean FIELD_2;

  public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd()
  {
  }

  public Object newInstance(String paramString1, String paramString2, boolean paramBoolean)
  {
    return new f32401fd(paramString1, paramString2, paramBoolean);
  }

  public BeanCopier$BeanCopierKey$$KeyFactoryByCGLIB$$f32401fd(String paramString1, String paramString2, boolean paramBoolean)
  {
    this.FIELD_0 = paramString1;
    this.FIELD_1 = paramString2;
    this.FIELD_2 = paramBoolean;
  }

  public int hashCode()
  {
    (95401 * 54189869);
  }

  public boolean equals(Object paramObject)
  {
    if ((paramObject instanceof f32401fd))
    {
      String tmp18_15 = ((f32401fd)paramObject).FIELD_0;
      String tmp18_8 = this.FIELD_0;
      if (tmp18_15 == null)
      {
        if (tmp18_8 != null)
          break label35;
        tmp18_15;
        tmpTernaryOp = tmp18_8;
        break label45;
      }
    }
  }

  public String toString()
  {
    String tmp11_8 = this.FIELD_0;
    if (tmp11_8 != null)
      tmpTernaryOp = new StringBuffer().append(tmp11_8.toString());
  }
}

这样就没有得到想要的那个class文件呢。麻烦您看一下,谢谢!@
11 楼 agapple 2010-11-09  
godtiger 写道
希望lz能够在讲的详细点 看着还是有些迷糊


可以看下 cglib相关性能测试对比http://www.iteye.com/topic/801577

那里有一些具体的使用例子,对照例子看可能更容易理解
10 楼 godtiger 2010-11-03  
希望lz能够在讲的详细点 看着还是有些迷糊
9 楼 qfstudying 2010-11-03  
agapple 写道
fuyou001 写道
问下楼主,楼主的uml 图是cglib项目自带的,还是楼主自己画的,如果是自己画的,可否告诉是用什么软件画的吗,谢谢


用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。

如果windows可以用用startuml,也是免费的。


推荐一个UML工具Visual Paradigm ,社区版是免费的,也是Java写的,跨平台。
下载地址:http://www.visual-paradigm.com/download/vpuml.jsp?edition=ce
8 楼 agapple 2010-11-02  
yangguo 写道
投个精华走人。一年后再来研究。


多谢支持。

对类似jvm字节码的处理学习上的确有点门槛,类似的还有javassist,BCEL,asm

毕竟文档相对少,最好是有一定的业务需求,再去理解一下它的适用场景,再结合一下反编译工具,掌握起来会相对更快
7 楼 yangguo 2010-11-02  
投个精华走人。一年后再来研究。
6 楼 agapple 2010-11-02  
fuyou001 写道
问下楼主,楼主的uml 图是cglib项目自带的,还是楼主自己画的,如果是自己画的,可否告诉是用什么软件画的吗,谢谢


用的是jude免费版http://jude.change-vision.com/jude-web/index.html,纯java的写的,所以具有跨平台性, linux下使用比较合适。

如果windows可以用用startuml,也是免费的。
5 楼 fuyou001 2010-11-02  
问下楼主,楼主的uml 图是cglib项目自带的,还是楼主自己画的,如果是自己画的,可否告诉是用什么软件画的吗,谢谢
4 楼 shmily2038 2010-11-02  
看的不是很懂,不过谢谢分享
3 楼 agapple 2010-11-02  
<div class="quote_title">lirong1978 写道</div>
<div class="quote_div">我也看不懂,我只求怎么去用它,至于它内部是什么样子,我就不管它了,当然大牛除外了.</div>
<p><br><br>一般常用的也就是 BeanCopier, BulkBean, BeanMap, FastClass, Enhancer(MethodInterceptor)。<br>例子上面应该都有一部分代码,主要是针对其适用场景和不适用的场景。<br><br>简单的概括几个常用的类: <br><br>BeanCopier:适用两个Pojo Bean之间,<span style="color: red;">所有属性</span>的全复制,两边的source和target的属性可以不一致,以setter方法为准,调用getter方法获取数据。<br><br>BulkBean : 相比于BeanCopier,它可以指定<span style="color: red;">特定的一组属性</span>进行处理,然后可以调用getter,setter方法进行属性拷贝。一般会应用在动态注入属性是通过xml配置时,特别有用,拷贝特定的属性到指定的目标对象上。<br><br>BeanMap : 相比于BeanCopier和BulkBean,它可以用于<span style="color: #ff0000;">解决pojo bean和map之间的转化</span>,比如将一个map的属性赋值给一个bean上,很方便把。<br><br>FastClass : 相比于java class对象,它利用了所谓的Index[]数组进行存储method,constructor。实现method.invoke实际上是先找到FastClass存储的index下标,然后进行调用。使用上和Class没什么特殊的区别,就是<span style="color: #ff0000;">多了这么一个index下标</span>概念。<br><br>Enhancer: 一般知道个MethodInterceptor就差不多了,其他的callback可以不关注。目前直接写Enhancer应该不太多,用spring aop会比较多。</p>
<p> </p>
2 楼 shaozhuang.liu 2010-11-02  
cglib都不维护了,不太了解spring的情况,反正现在hibernate已经不推荐使用cglib了,因为有两个bug,我们都给cglib的团队提供了patch了,也没有得到任何回应,看cglib的提交记录可以发现都两年多没有活动了
还是看看javassist吧:D
1 楼 agapple 2010-11-02  
<p><span style="font-size: small;">再可以来对照下spring aop中与cglib的结合,核心类: Cglib2AopProxy</span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">看几个ProxyFactoryBean中跟cglib相关的参数: </span><span style="color: #ff0000;"><span style="font-size: small;">来自于spring 1.x版本的文档描述</span></span></p>
<p> </p>
<ol>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;">   proxyTargetClass</span></code><span style="font-size: small;">:这个属性为</span><code class="literal"><span style="font-size: small;">true</span></code><span style="font-size: small;">时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为</span><code class="literal"><span style="font-size: small;">true</span></code><span style="font-size: small;">,CGLIB代理将被创建</span></span></li>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;">   optimize</span></code><span style="font-size: small;">:用来控制</span><span class="emphasis" style="color: #000000;"><em><span style="font-size: small;">通过CGLIB创建</span></em></span><span style="font-size: small;">的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理;对于JDK动态代理(缺省代理)无效。</span></span></li>
<li><span style="font-family: Verdana, Arial, sans-serif;"><code class="literal"><span style="font-size: small;">   frozen</span></code><span style="font-size: small;">:用来控制代理工厂被配置之后,是否还允许修改通知。缺省值为</span><code class="literal"><span style="font-size: small;">false</span></code><span style="font-size: small;">(即在代理被配置之后,不允许修改代理的配置)。</span></span></li>
</ol>
<p> </p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">以前一直不明白</span><span style="font-family: monospace;"><span style="font-size: small;">optimize和frozen的使用</span></span><span style="font-family: monospace;"><span style="font-size: small;">,正好借这次</span><span style="font-family: Verdana, Arial, Helvetica, sans-serif;"><span style="font-size: small;">Cglib2AopProxy的学习。</span></span></span></p>
<p><span style="font-size: small;"><br></span></p>
<p><span style="font-size: small;">ProxyCallbackFilter : 核心的callback的调度逻辑</span></p>
<p> <span style="background-color: #fafafa; white-space: pre;">&lt;dt&gt;For exposed proxies&lt;/dt&gt;</span></p>
<pre name="code" class="官方javaodc描述">&lt;dd&gt;Exposing the proxy requires code to execute before and after the
method/chain invocation. This means we must use
DynamicAdvisedInterceptor, since all other interceptors can avoid the
need for a try/catch block&lt;/dd&gt;
&lt;dt&gt;For Object.finalize():&lt;/dt&gt;
&lt;dd&gt;No override for this method is used.&lt;/dd&gt;
&lt;dt&gt;For equals():&lt;/dt&gt;
&lt;dd&gt;The EqualsInterceptor is used to redirect equals() calls to a
special handler to this proxy.&lt;/dd&gt;
&lt;dt&gt;For methods on the Advised class:&lt;/dt&gt;
&lt;dd&gt;the AdvisedDispatcher is used to dispatch the call directly to
the target&lt;/dd&gt;
&lt;dt&gt;For advised methods:&lt;/dt&gt;
&lt;dd&gt;If the target is static and the advice chain is frozen then a
FixedChainStaticTargetInterceptor specific to the method is used to
invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
used.&lt;/dd&gt;
&lt;dt&gt;For non-advised methods:&lt;/dt&gt;
&lt;dd&gt;Where it can be determined that the method will not return &lt;code&gt;this&lt;/code&gt;
or when &lt;code&gt;ProxyFactory.getExposeProxy()&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;,
then a Dispatcher is used. For static targets, the StaticDispatcher is used;
and for dynamic targets, a DynamicUnadvisedInterceptor is used.
If it possible for the method to return &lt;code&gt;this&lt;/code&gt; then a
StaticUnadvisedInterceptor is used for static targets - the
DynamicUnadvisedInterceptor already considers this.&lt;/dd&gt;
&lt;/dl&gt;</pre>
<p> </p>
<p>里面针对haveAdvice和isFrozen做了些优化</p>
<p>1. 如果是finalize方法,直接返回NoOp,不做任何代理</p>
<p>2. 如果proxy对象是spring advised的子类,则通过cglib的Dispatcher,直接委托给advisor对象进行方法调用</p>
<p>3. 如果是equals或者hashcode方法,委托给对应的EqualsInterceptor,HashCodeInterceptor,不会走入spring的inteceptor chian。</p>
<p>4. 如果spring没有配置advise,则走了cglib的MethodInterceptor进行处理,直接delegate到相关proxy.method上(例如:StaticUnadvisedExposedInterceptor,DynamicUnadvisedExposedInterceptor会区分targetSource是否是单例,exposeProxy属性做相应的优化),也<span style="background-color: #ffffff;">不会走入spring的inteceptor chian。</span></p>
<p>5. <span style="background-color: #ffffff;">如果spring配置了advise , 而且是<span style="background-color: #ffffff;">isFrozen,默认值为false,这样会走到基于cglib MethodInterceptor的DynamicAdvisedInterceptor进行处理。</span></span></p>
<p>6. 如果spring配置了advise,还会区分是否是isFrozen=true(inteceptor chian不会动态变化), 这样会根据事先分析好的method,比如有些method不是Advisor的pointcut匹配目标,则会直接走Dispatcher进行处理,不做代理。</p>
<p> </p>
<p> </p>
<p><span style="background-color: #ffffff;">spring的advise的一系列inteceptor,都是每次请求动态调用advised.getInterceptorsAndDynamicInterceptionAdvice获取,然后涉及global inteceptor(.*匹配) , pointcut filter(方法匹配), inteceptor等处理。</span></p>
<p> </p>
<p><span style="background-color: #ffffff;">isFrozen的功能相信大家应该能明白了,但很奇怪我没找到<span style="font-family: monospace; font-size: small;">optimize在aop相关代码中的使用,搜了下,只看到了这么一处使用而已</span></span></p>
<p>
</p>
<pre name="code" class="java">public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
return CglibProxyFactory.createCglibProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}</pre>
 我用的spring版本为2.5.5,估计这份中文文档是spring 1.x的,难不成spring team已经做了代码优化和重构? 知道的人可以解答下
<p> </p>

相关推荐

    动态代理cglibjar包和源码

    本文将深入探讨CGLIB的工作原理、使用方式以及源码分析。 CGLIB是一个强大的高性能的代码生成库,它可以在运行期扩展Java类与实现Java接口。CGLIB通过字节码操作技术,在运行时动态生成新的子类来实现对目标类的...

    cglib-3.1源码

    9. **源码学习价值**:CGLIB的源码是一个很好的学习字节码操作和代理技术的实例。通过阅读源码,可以深入理解Java字节码的生成过程,以及AOP的实现原理。 总的来说,CGLIB 3.1源码不仅为我们提供了实现动态代理的...

    cglib-2.2.2源码

    cglib-2.2.2源码

    Spring源码出错,缺失cglib.3.2.6 与 objenesis.2.6

    理解CGLIB和Objenesis的工作原理以及它们在Spring框架中的作用,对于深入学习和使用Spring具有重要意义。这两个库不仅帮助Spring实现了核心功能,还为其他需要动态生成类或实例化的Java项目提供了强大的支持。因此,...

    cglib包,源代码等

    **CGLIB(Code Generation Library)** 是一个强大的高性能的代码生成库,它可以在运行期扩展Java类与实现Java接口。...通过深入学习和理解CGLIB的工作机制,开发者可以更好地利用它来解决实际问题。

    CGLib3.2.5依赖包及源码

    源码包提供了CGLib的实现细节,包括`cglib-RELEASE_3_2_5.tar.gz`和`cglib-RELEASE_3_2_5.zip`两种格式。这些源码对于开发者来说极其宝贵,因为它们允许深入理解CGLib的工作原理,有助于自定义扩展或调试遇到的问题...

    cglib.jar源码

    **CGLIB (Code Generation Library)** 是一个Java库,它允许开发者在运行时动态地创建新的类...通过阅读和分析`cglib.jar`的源码,开发者可以更深入地理解CGLIB的工作原理,从而更好地利用它来提升应用的性能和灵活性。

    cglib 源代码分析(原创)

    通过阅读和分析源码,我们可以了解到字节码生成的具体步骤,学习到如何利用ASM进行字节码操作,以及如何在Java中实现高效动态代理。总的来说,CGlib是Java开发中的一个强大工具,对于理解和实现复杂的企业级功能至关...

    cglib_cglib.zip

    CGLib,全称为Code Generation Library,是一个强大的Java代码生成库,广泛用于动态代理、AOP...这个“cglib.zip”压缩包提供的内容,无论是源码还是文档,都值得我们去研究和学习,以便更好地利用CGLib解决实际问题。

    CGLIB依赖jar包

    CGLIB,全称为Code Generation Library,是一个强大的高性能的代码生成库,它在Java世界里被广泛应用,尤其是在Spring框架中。CGLIB是基于ASM(一个底层的Java字节码操作和分析框架)来实现的,它允许开发者在运行时...

    cglib.jar下载

    CGLIB介绍与原理(部分节选自网络) 一、什么是CGLIB? CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要...

    cglib_cglib3.2_remain3gr_cglibjar包_cglib.jar_

    【CGLib:强大的Java代码生成库】 CGLib(Code Generation Library)是一个强大的、高性能的代码生成库,它在运行期扩展Java类与实现Java接口。这个库最初是为EJB的透明代理而设计的,但后来发展成为了一个广泛的...

    cglib的依赖包

    CGlib,全称为Code Generation Library,是一个强大的Java代码生成库,广泛用于动态代理、AOP(面向切面编程)框架以及性能优化等场景。它通过字节码技术为类创建子类,从而实现对目标类的功能增强。在Java中,由于...

    CGLIB需要的asm-2.2.3.jar和cglib-nodep-2.2.jar

    4. **类的扩展**:CGLIB允许在运行时动态地生成新的类并扩展已有类的功能,这在某些需要动态增强功能或者无法修改源码的情况下非常有用。 在实际使用中,我们需要将ASM-2.2.3.jar和CGLIB-nodep-2.2.jar这两个库引入...

    spring源码编译时缺少的类cglib,objenesis,castor

    - **CGLIB** 提供了Spring AOP的基础,允许我们在不修改源码的情况下,对方法进行拦截,实现事务管理、日志记录等切面功能。 - **Objenesis** 提高了Spring在处理代理对象创建时的灵活性,特别是在测试和复杂场景下...

    cglib及其依赖包

    CGLib,全称为Code Generation Library,是一个强大的高性能的代码生成库,它在Java世界中被广泛应用,尤其是在动态代理和AOP(面向切面编程)领域。这个库的主要功能是能够在运行时动态创建类或者增强已有类的功能...

    cglib.jar以及cglib-src.jar

    CGLib,全称为Code Generation Library,是一个强大的高性能的代码生成库,它在Java世界中被广泛应用,尤其...通过深入学习CGLIB的源码,开发者可以更好地掌握Java的动态代理机制,提高自己的编程技巧和问题解决能力。

    开发工具 cglib-3.2.4

    开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4开发工具 cglib-3.2.4...

    cglib2.2.jar

    CGLIB,全称为Code Generation Library,是一个非常强大的Java字节码操纵和动态代理框架。它在Java编程中扮演着重要角色,特别是在AOP(面向切面编程)和ORM(对象关系映射)框架中,如Spring AOP和Hibernate。这个...

Global site tag (gtag.js) - Google Analytics