cglib动态代理对类没有任何限制吗?

2,278 阅读2分钟

大家都知道,动态代理常见的两种实现方式,jdk的动态代理和cglib动态代理,而jdk动态代理必须基于接口,cglib没有这样的要求。之前我们讨论过jdk动态代理为什么必须基于接口,传送门:

那么cglib动态代理真的没有任何限制吗?

首先,先来用cglib实现一个动态代理

被代理对象

public class Student {
    public void say(){
        System.out.println("i'm student");
    }
}

代理类

public class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before dosomething...");
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        System.out.println("after dosomething...");
        return object;
    }  
}

测试类

public static void main(String[] args) {
   System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "");
   Enhancer enhancer = new Enhancer();
   //设置目标类的字节码文件
   enhancer.setSuperclass(Student.class);
   //设置回调函数
   enhancer.setCallback(new MyMethodInterceptor());
   //这里的create方法就是正式创建代理类
   Student proxy = (Student)enhancer.create();
   proxy.say();
}

运行结果

before dosomething...
i'm student
after dosomething...

发现动态代理生效了。

动态代理获取原类(被代理类),通常需要通过继承和接口实现来进行获取,如下

Student proxy = (Student)enhancer.create();

因此我们猜测,cglib代理应该是通过继承来实现代理的。

先做个实验,由于java当中有fianl关键字,可以修饰类,表示该类不可被继承,所以将被代理类修改如下:

public final class Student {
    public void say(){
        System.out.println("i'm student");
    }
}

再执行之前的测试类,执行结果如下,可以看出确实出错了,并且提示不能继承fianl类,说明我们分析正确。

Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class com.example.demo.cglib.Student
	at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:565)
	at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
	at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:332)
	at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:96)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:94)
	at net.sf.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at net.sf.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
	at net.sf.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:119)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
	at com.example.demo.cglib.Test.main(Test.java:16)

下面我们再分析下cglib的源码,证明上面的过程。

首先从enhancer.create()方法开始,只保留关键方法

public class Enhancer extends AbstractClassGenerator {
  ……
  public Object create() {
    ……
    return this.createHelper();
  }

  private Object createHelper() {
          this.preValidate();
    ……
    Object result = super.create(key);
    return result;
  }
}

接着看super.create(key)方法

public abstract class AbstractClassGenerator<T> implements ClassGenerator {
protected Object create(Object key) {
       ……
       // 获取代理对象
       Object obj = data.get(this, this.getUseCache());
       return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
    }
}

public Object get(AbstractClassGenerator gen, boolean useCache) {
  if (!useCache) {
    return gen.generate(this);
  } else {
    Object cachedValue = this.generatedClasses.get(gen);
    return gen.unwrapCachedValue(cachedValue);
  }
}

protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
    ……
    // 生成代理类的字节
    byte[] b = this.strategy.generate(this);
    ……    
}

最终调用到下面方法

public byte[] generate(ClassGenerator cg) throws Exception {
  DebuggingClassWriter cw = this.getClassVisitor();
  this.transform(cg).generateClass(cw);
  // 生成字节
  return this.transform(cw.toByteArray());
}

此时再看cw.toByteArray()方法,可以看到DebuggingClassWriter.debugLocation可以配置代理类生成到指定路径。

public byte[] toByteArray() {
        return (byte[])((byte[])AccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                byte[] b = ((ClassWriter)DebuggingClassWriter.access$001(DebuggingClassWriter.this)).toByteArray();
              // 如果为true,则可以输出代理类到 DebuggingClassWriter.debugLocation 位置
              if (DebuggingClassWriter.debugLocation != null) {
                    String dirs = DebuggingClassWriter.this.className.replace('.', File.separatorChar);

                    try {
                        (new File(DebuggingClassWriter.debugLocation + File.separatorChar + dirs)).getParentFile().mkdirs();
                        File file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".class");
                        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));

                        try {
                            out.write(b);
                        } finally {
                            out.close();
                        }

                        if (DebuggingClassWriter.traceCtor != null) {
                            file = new File(new File(DebuggingClassWriter.debugLocation), dirs + ".asm");
                            out = new BufferedOutputStream(new FileOutputStream(file));

                            try {
                                ClassReader cr = new ClassReader(b);
                                PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
                                ClassVisitor tcv = (ClassVisitor)DebuggingClassWriter.traceCtor.newInstance(null, pw);
                                cr.accept(tcv, 0);
                                pw.flush();
                            } finally {
                                out.close();
                            }
                        }
                    } catch (Exception var17) {
                        throw new CodeGenerationException(var17);
                    }
                }

                return b;
            }
        }));
    }

那么,我们将原来的测试类,修改如下:

public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/ganhuojun/code/my_demo/target");
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Student.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        //这里的create方法就是正式创建代理类
        Student proxy = (Student)enhancer.create();
        proxy.say();
    }

执行结果如下,并查看相应目录

CGLIB debugging enabled, writing to '/Users/ganhuojun/code/my_demo/target'
before dosomething...
i'm student
after dosomething...

打开代理类Student$$EnhancerByCGLIB$$8b03b040.class,可以看到确实继承了原类,因此cglib实现动态代理,要求类必须不能被fianl修饰

public class Student$$EnhancerByCGLIB$$8b03b040 extends Student implements Factory {
  //内容省略
  ……
}

结论:

cglib实现动态代理,要求类必须不能被fianl修饰。扩展一下,如果方法被final修饰,能否被代理执行呢?