cglib的最初版本干了什么?

因为翻阅spring的源码,看到overrides部分,牵扯到了cglib,加上后续马上要进行的aop也要用到这个玩意儿,其实知道一些,说是用继承的方法解决代理的问题。

跟jdkproxy经常拿来比较,当然这两个也是面试经常问的问题,当然还是那句话,就木有一个技术难点经得起细看, 都是点只是没看到的点而已,然后就会被人鄙视说这都不会,只想说,去你大爷的,老子只是没看。

好了废话说多无益,来看下最初的版本吧,往往最初的版本是最核心的东西,之后的种种不过是锦上添花,那这锦才是最需要研究的。

github上cglib的版本最早的是

2002年的版本,我们把代码下载下来只有三个类。

把源码拿到后,做了个例子看下,确实比较猛。

先看下代码:

/*
 * Factory.java
 *
 */

package proxy.net.sf.cglib.proxy;

/**
 *
 * @author  user
 */
  public interface Factory{
      
       public Object newInstance(MethodInterceptor ih);
    }
   
    
public interface MethodInterceptor {
    
    /** Generated code calls this method first
     * @param obj this
     * @param method Intercepted method
     * @param args Arg array
     * @throws Throwable  any exeption to stop execution
     * @return returned value used as parameter for all
     * interceptor methods
     */    
  /*  public Object beforeInvoke( Object obj,
                                java.lang.reflect.Method method,
                                Object args[] )throws java.lang.Throwable;
   */
    
    /** Generated code calls this method before invoking super
     * @param obj this
     * @param method Method
     * @param args Arg array
     * @param retValFromBefore value returned from beforeInvoke
     * @throws Throwable any exeption to stop execution
     * @return true if need to invoke super
     */    
    public boolean invokeSuper(Object obj,
                               java.lang.reflect.Method method,
                               Object args[]
            /*,Object retValFromBefore*/)
                                             throws Throwable;
    
    /** this method is invoked after execution
     * @param obj this
     * @param method Method
     * @param args Arg array
     * @param retValFromBefore value returned from beforeInvoke
     * @param invokedSuper value returned from invoke super
     * @param retValFromSuper value returner from super
     * @param e Exception thrown by super
     * @throws Throwable any exeption
     * @return value to return from generated method
     */    
    public Object afterReturn(Object obj,
                              java.lang.reflect.Method method,
                              Object args[],
            /*Object retValFromBefore,*/
                              boolean invokedSuper,
                              Object retValFromSuper,
                              Throwable e)
                                             throws Throwable;
    
    
}

另一个就是类:Enhance.java ,这个是核心类,内容比较多,这里就不贴出来了。后边我们的分析里,主要就是Enhance里的代码在做代码增强。

下边我们来看下我们写的测试的例子:

定义一个接口

package cglib.test;

public interface Call {

    void call();
    void eat();
}

定义实例:

package cglib.test;

public class Dog implements Call{

    @Override
    public void call() {
        System.out.println("wang!wang!");
    }

    @Override
    public void eat() {
        System.out.println("eat!eat!");
    }
}

测试类:

package cglib.test;

import proxy.net.sf.cglib.proxy.Enhancer;
import proxy.net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.Method;

public class CglibTest {


    public static void main(String args[]){
try {
            Dog dog = (Dog)Enhancer.enhance(Dog.class, null, new MethodInterceptor() {
                @Override
                public boolean invokeSuper(Object obj, Method method, Object[] args) throws Throwable {

                    if(method.getName().equals("eat")){
                        System.out.println("before eat invoke ...");
                    }else if(method.getName().equals("call")){
                        System.out.println("before call invoke ...");
                    }
                    return true;
                }

                @Override
                public Object afterReturn(Object obj, Method method, Object[] args, boolean invokedSuper, Object retValFromSuper, Throwable e) throws Throwable {
                    System.out.println("after invoke ...");
                    return obj;
                }
            }, CglibTest.class.getClassLoader());

            dog.call();
            dog.eat();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

    }

}

测试结果:

before call invoke ...
wang!wang!
after invoke ...
before eat invoke ...
eat!eat!
after invoke ...

可以看到已经根据我们实现的MethodInteceptor进行了增强,输出我们想要的内容了。

嗯,代码拿下来后,我是新创建的工程放入代码的,发现有编译错误,查看import的错误,是缺少bcel jar包。去apache官网上下载一个相对比较老的5.2的版本。

没错就是这个包,查了下,就是用来生成增强类的。只不过后来cglib的这块儿引用被asm替换掉了,当然asm应该是比bcel更强悍的。不过既然要看最初的版本,那我们就还是按照这个版本来看吧。那就避免不了要看bcel的东西了。

花点时间跟踪下bcel的代码我们来看下最终生成的类:

生成的类名字:cglib.test.Dog$$EnhancedBySimplestore$$0

生成的类的call方法:

重点看下:21步/32步/53步

其实已经很明显了,增强之后的类,call方法调用的时候,增加了我们定义的那个MethodInteceptor的两个方法,一个在call调用前调用,一个在call调用之后调用。

至于,其他的方法我们着重看下:构造函数

构造函数写的比较明显了,这里要说明的是代码增强里,添加了属性: public MethodInteceptor h;

构造函数里,除了调用父类的构造函数外,就是把构造函数的参数里的 MethodInteceptor赋值给这个h;

翻成代码就是:

public Dog$$EnhancedBySimplestore$$0(MethodInteceptor h) {

  super();

  this.h = h;

}

然后,我们来看添加的另一个方法,另一个方法就是接口Factory的实现方法:

也比较简单,就是新建一个增强类的实例,然后调用它的<init>方法。然后把这个对象返回。其实就是创建增强累的实例。

翻译过来就是:

public Dog newInstance(MethodInteceptor h){

  return new Dog$$EnhanceBySimplestore$$0(h).init();

}

可能翻译的不太准确,但意思就是这个意思了。

至此我们就把Enhance增强代码做的事情,基本原理弄明白了。

写的比较简单,看了一下Enhance的代码,但其实看了个大概已经花了一两个钟头的时间了,期间做的代码增强的事情,还是挺强的。其实核心在于对于Class类结构的熟知以及对Bcel的使用。有了这两样东西,加上耐心,把cglib的事情做掉就并不是难事了。

再来理解下cglib的这个最简化版本的内容,我们发现,其实我们把Enhance这个具体的实现类去除后,就是两个接口类。

那么这两个接口类就比较有意思了,一个是一个返回实例类的接口:

public Object newInstance(MethodInterceptor ih);

把MethodInteceptor实例作为参数,构造出来一个对象。

一个是具体的MethodInteceptor接口本身,这个接口有两个方法,一个是invokeSuper,一个是afterReturn。

思考一下,cglib的具体做的事情无外乎就是:

1:为增强类添加一个MethodInteceptor的属性h;

2:为增强类添加一个以MethodInteceptor接口的实例为参数的构造函数,并将MethodInteceptor的实例赋值给1步骤创建的属性h。

3:为增强类添加一个以MethodInteceptor接口的实例为参数的newInstance方法,这个方法里调用2步骤添加的构造函数,创建出增强累。

4:为原始类的方法,增加增强实现。即在调用原始方法的前后,调用属性h即传入的MethodInteceptor的实例的那两个invokeSuper和afterReturn方法。

思路,就是这么简单。把我们的MethodInteceptor的实现,增强到原始类的方法中,就可以了。借助于代码增强,和继承的特性来实现了这个方案。

解析到这里就结束了,相信cglib的后续版本做了很多的优化和其他相关功能的添加。但最核心的这个事情应该是没什么大变动的,后续有时间再专门写一篇来对比最新的版本做了那些事情。跟现在这个版本的核心代码有什么变动。

原文地址:https://www.cnblogs.com/aquariusm/p/11180522.html