cglib原理分析

先写个示例

public class MyInterceptor implements MethodInterceptor {
	public Object getProxy(Class<?> clzss) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(clzss);
		// 设置回调方法
		enhancer.setCallback(this);
		// 创建代理对象
		return enhancer.create();
	}
	@Override
	public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println(method.getName());
		System.out.println("事务开始。。。");
//		Object result = method.invoke(object, args); //死循环,内存溢出
//		Object result = methodProxy.invoke(object, args);//死循环,内存溢出
		Object result = methodProxy.invokeSuper(object, args);
		System.out.println("事务结束。。。");
		return result;
	}
}


public class CglibTest {
	public static void main(String[] args) {
		// 将cglib生成的class写入到E:\\classes文件夹中
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\classes");
		MyInterceptor interceptor = new MyInterceptor();
		CglibTest cglibTest = (CglibTest) interceptor.getProxy(CglibTest.class);
		cglibTest.operate1("zhangsan");
	}
	public String operate1(String name) {
		System.out.println("操作数据库1");
		return name;
	}
}

运行结果:

CGLIB debugging enabled, writing to 'E:\classes'
operate1
事务开始。。。
操作数据库1
事务结束。。。

打开E:\classes文件夹
在这里插入图片描述
打开CglibTest$$EnhancerByCGLIB$$9d922c18.class分析下源码

public class CglibTest$$EnhancerByCGLIB$$9d922c18 extends CglibTest
  implements Factory
{
  private boolean CGLIB$BOUND;
  public static Object CGLIB$FACTORY_DATA;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static Object CGLIB$CALLBACK_FILTER;
  private static final Method CGLIB$operate1$0$Method;
  private static final MethodProxy CGLIB$operate1$0$Proxy;
//省略

  static void CGLIB$STATICHOOK1()
  {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class localClass1 = Class.forName("com.zhd.basics.javabase.proxy.CglibTest$$EnhancerByCGLIB$$9d922c18");
    Class localClass2;
    Method[] tmp50_47 = ReflectUtils.findMethods(new String[] { "operate1", "(Ljava/lang/String;)Ljava/lang/String;" }, (localClass2 = Class.forName("com.zhd.basics.javabase.proxy.CglibTest")).getDeclaredMethods());
    CGLIB$operate1$0$Method = tmp50_47[0];
    CGLIB$operate1$0$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)Ljava/lang/String;", "operate1", "CGLIB$operate1$0");
    tmp50_47;
//省略
  }

  final String CGLIB$operate1$0(String paramString)
  {
    return super.operate1(paramString);
  }

  public final String operate1(String paramString)
  {
    MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
    if (tmp4_1 == null)
    {
      tmp4_1;
      CGLIB$BIND_CALLBACKS(this);
    }
    MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
    if (tmp17_14 != null)
      return (String)tmp17_14.intercept(this, CGLIB$operate1$0$Method, new Object[] { paramString }, CGLIB$operate1$0$Proxy);
    return super.operate1(paramString);
  }
//省略
}

cglib会生成代理类CglibTest$$EnhancerByCGLIB$$9d922c18继承CglibTest,父类中的每个方法在子类中都有两个方法与之对应,在静态方法中同时也生成代理方法。

分析一下代码的执行过程

当main函数执行到cglibTest.operate1(“zhangsan”);这个语句时

  1. 首先会进入到代理类中的public final String operate1(String
    paramString)方法。进而执行了tmp17_14.intercept(…)方法。
  2. 根据MyInterceptor中的intercept,先是会输出一句“事务开始。。。”,然后调用methodProxy.invokeSuper(object, args),这里的methodProxy对应的是tmp17_14.intercept(this, CGLIB$operate1 0 0 0Method, new Object[] { paramString }, CGLIB$operate1 0 0 0Proxy);中的CGLIB$operate1 0 0 0Proxy。
  3. 最后输出一句“事务结束。。。”。

拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的,MethodProxy非常关键,我们分析一下它具体做了什么。

public class MethodProxy {
    private Signature sig1;
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo;
    private final Object initLock = new Object();
    private volatile MethodProxy.FastClassInfo fastClassInfo;
    //c1:被代理对象Class
    //c2:代理对象Class
    //desc:入参类型
    //name1:被代理方法名
    //name2:代理方法名
    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);//被代理方法签名
        proxy.sig2 = new Signature(name2, desc);//代理方法签名
        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
        return proxy;
    }

private static class CreateInfo {
    Class c1;
    Class c2;
    NamingPolicy namingPolicy;
    GeneratorStrategy strategy;
    boolean attemptLoad;

    public CreateInfo(Class c1, Class c2) {
        this.c1 = c1;
        this.c2 = c2;
        AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
        if(fromEnhancer != null) {
            this.namingPolicy = fromEnhancer.getNamingPolicy();
            this.strategy = fromEnhancer.getStrategy();
            this.attemptLoad = fromEnhancer.getAttemptLoad();
        }

    }
}
  • invokeSuper调用
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }

private static class FastClassInfo {
    FastClass f1;//被代理类FastClass
    FastClass f2;//代理类FastClass
    int i1; //被代理类的方法签名(index)
    int i2;//代理类的方法签名

    private FastClassInfo() {
    }
}

上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class文件吗?CglibTest$$EnhancerByCGLIB$$9d922c18$$FastClassByCGLIB$$79193b26.class就是代理类的FastClass,
CglibTest$$FastClassByCGLIB$$52c6331c.class就是被代理类的FastClass。

Fastclass 机制分析

Jdk动态代理的拦截对象是通过反射的机制来调用被拦截方法的,反射的效率比较低,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,下面用一个小例子来说明一下,这样比较直观:

public class FastclassTest {
	public static void main(String[] args) {
		Test1 t1 = new Test1();
		Test2 fc = new Test2();
		int index = fc.getIndex("f()V");
		fc.invoke(index, t1, null);
	}
}

class Test1 {
	public void f() {
		System.out.println("f method");
	}

	public void g() {
		System.out.println("g method");
	}
}

class Test2 {
	public Object invoke(int index, Object o, Object[] ol) {
		Test1 t = (Test1) o;
		switch (index) {
		case 1:
			t.f();
			return null;
		case 2:
			t.g();
			return null;
		}
		return null;
	}

	public int getIndex(String signature) {
		switch (signature.hashCode()) {
		case 3078479:
			return 1;
		case 3108270:
			return 2;
		}
		return -1;
	}
}

运行结果:

f method

上例中,Test2是Test1的Fastclass,在Test2中有两个方法getIndex和invoke。在getIndex方法中对Test的每个方法建立索引,并根据入参(方法名+方法的描述符)来返回相应的索引。Invoke根据指定的索引,以ol为入参调用对象O的方法。这样就避免了反射调用,提高了效率。代理类(CglibTest$$EnhancerByCGLIB$$9d922c18)中与生成Fastclass相关的代码如下:

 Class localClass1 = Class.forName("com.zhd.basics.javabase.proxy.CglibTest$$EnhancerByCGLIB$$9d922c18");
    Class localClass2;
    Method[] tmp50_47 = ReflectUtils.findMethods(new String[] { "operate1", "(Ljava/lang/String;)Ljava/lang/String;" }, (localClass2 = Class.forName("com.zhd.basics.javabase.proxy.CglibTest")).getDeclaredMethods());
    CGLIB$operate1$0$Method = tmp50_47[0];
    CGLIB$operate1$0$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/String;)Ljava/lang/String;", "operate1", "CGLIB$operate1$0");

MethodProxy中会对localClass1和localClass2进行分析并生成FastClass,然后再使用getIndex来获取方法g 和 CGLIB$g$0的索引,具体的生成过程将在后续进行介绍,这里介绍一个关键的内部类:

private static class FastClassInfo
    {
        FastClass f1; // net.sf.cglib.test.Target的fastclass
        FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass
        int i1; //方法g在f1中的索引
        int i2; //方法CGLIB$g$0在f2中的索引
    }

MethodProxy 中invokeSuper方法的代码如下:

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

当调用invokeSuper方法时,实际上是调用代理类的CGLIB$g 0 方 法 , C G L I B 0方法,CGLIB 0CGLIBg$0直接调用了目标类的g方法。所以,只能用invokeSuper方法来调用被拦截的目标类方法,如果用invoke方法就是陷入死循环,导致OOM。

注意:cglib不能代理final修饰的类,因为final修饰的类不能被继承。
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值