JDK动态代理和CGLIB动态代理浅析

代理模式浅析

1.什么是代理模式

JDK动态代理和CGLIB动态代理浅析
什么是代理模式呢,首先举个简单的例子,张三(用户)想要一张演唱会的门票但是自己没有渠道购买,这时他的朋友李四(代理人)说他能够买到演唱会的门票(被代理方法),张三只需要请李四帮忙购买门票即可,至于李四是怎么买的通过什么方式买的张三并不需要知道。代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。使用代理模式主要有两个目的:一是保护目标对象,而是增强目标对象。

代理模式类图

JDK动态代理和CGLIB动态代理浅析

Subject 是顶层接口,RealSubject 是真实对象(被代理对象),Proxy 是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象方法,同时也调用被代理对象的方法,但是在代理对象前后增加一些处理。在代码中,我们想到代理,就会理解为是代码增强,其实就是在原本逻辑前后增加一些逻辑,而调用者无感知。代理模式属于结构型模式,有静态代理和动态代理。

2.静态代理

什么是静态代理,其实这个动静区别是对于程序来说的,所谓静态也就是在程序运行之前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就已经确定了。下面用代码写一个简单的静态代理示例。

public class Order {
    private Object orderInfo;
    private Long createTime;
    private String id;

    public Object getOrderInfo() {
        return orderInfo;
    }

    public void setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
    }

    public Long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}
public class OrderService implements IOrderService {
    private OrderDao orderDao;

    public OrderService() {
		//如果使用 Spring 应该是自动注入的
		//我们为了使用方便,在构造方法中将 orderDao 直接初始化了
        orderDao = new OrderDao();
    }

    @Override
    public int createOrder(Order order) {
        System.out.println("OrderService 调用 orderDao 创建订单");
        return orderDao.insert(order);
    }
}
public interface IOrderService {
    int createOrder(Order order);
}

有个IOrderService接口,里面有个创建订单的方法,OrderService实现了IOrderService接口,并实现了创建订单的这个方法。现在在分布式环境中,需要对数据库进行分库分表,分库分表进行完之后使用JAVA操作时,就需要配置多个数据源,我们通过设置数据源路由来动态切换数据源。根据开闭原则,已经写好的逻辑不改动,通过代理对象的方式进行修改,我们主要完成的功能是根据订单创建时间自动按年进行分库。通过ThreadLocal编写DynamicDataSourceEntry(被代理类)动态切换数据源的类。

/**
 * @author: Winston
 * @createTime: 2021/6/15
 * 动态切换数据源
 */
public class DynamicDataSourceEntry {
    // 默认数据源
    public final static String DEFAULT_SOURCE = null;
    private final static ThreadLocal<String> local = new ThreadLocal<String>();

    private DynamicDataSourceEntry(){}

    /**
     * 清空数据源
     */
    public static void clear(){
        local.remove();
    }

    /**
     * 获取当前正在使用的数据源名称
     * @return
     */
    public static String get(){
       return local.get();
    }

    /**
     *还原当前切面的数据源
     */
    public static void restore(){
        local.set(DEFAULT_SOURCE);
    }

    /**
     * 设置已知名字的数据源
     * @param source
     */
    public static void set(String source){
        local.set(source);
    }

    /**
     * 根据年份动态设置数据源
     * @param year
     */
    public static void set(int year) {
        local.set("DB_" + year);
    }
}

接下来是编写代理类实现IOrderService接口,通过这个代理类调用被代理类同时完成对被代理类的方法调用,在已有功能的基础上,不将业务代码修改,完成数据源切换的工作。

/**
 * @author: Winston
 * @createTime: 2021/6/15
 */
public class OrderServiceStaticProxy implements IOrderService {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private IOrderService orderService;

    public OrderServiceStaticProxy(IOrderService orderService){
        this.orderService = orderService;
    }

    @Override
    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");
        DynamicDataSourceEntry.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }

    private void before(){
        System.out.println("Proxy before method.");
    }
    private void after(){
        System.out.println("Proxy after method.");
    }
}

测试代码

/**
 * @author: Winston
 * @createTime: 2021/6/15
 */
public class StaticProxyTest {
    public static void main(String[] args) throws ParseException {
        Order order = new Order();
// Date today = new Date();
// order.setCreateTime(today.getTime());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2017/02/01");
        order.setCreateTime(date.getTime());
        IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
        orderService.createOrder(order);

    }
}

运行结果

Proxy before method.
静态代理类自动分配到【DB_2017】数据源处理数据。
OrderService 调用 orderDao 创建订单
OrderDao 创建 Order 成功!
Proxy after method.

静态代理小结

优点:不需要修改业务类的代码,业务类只需要关注业务本身,这是代理共有的优点

缺点:

  1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,比如要给所有方法都加上打印日志这一操作,那么需要对所有方法都要进行代理,因此静态代理在程序规模稍大时就无法胜任了。
  2. 如果接口新增一个方法,除了所有实现类要实现该方法外,所有代理类也要实现该方法,增加了代码维护的难度。

2.动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。如果以找对象为例,父亲给儿子找对象,父亲就好比是静态代理,只为儿子找对象,不管别人是否要找对象,而使用动态代理能够适应复杂的业务场景,媒婆就好比是动态代理,能够为任何单身人士寻找对象不仅限于儿子。同样我们用代码写一个动态代理的示例.

JDK动态代理

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public interface Person {

    /**
     * 相亲方法
     */
    public void findLove();
}

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class Girl implements  Person{

    /**
     * 找对象要求
     */
    @Override
    public void findLove() {
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("体重73kg");
        System.out.println("家里两套房");
    }
}
/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JDKMeipo implements InvocationHandler {

    /**
     * 被代理对象
     */
    private Object target;

    public Object getInstance(Object target){
        this.target = target;
        // 获取class信息
        Class clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    /**
     * 前置方法
     */
    public void before(){
        System.out.println("我是媒婆,现在准备开始为你寻找条件合适的相亲对象");
    }

    /**
     * 后置方法
     */
    public void after(){
        System.out.println("条件合适的话就准备嘿嘿嘿");
    }
}

测试方法

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyTest {
    public static void main(String[] args) {

        Person person = (Person) new JDKMeipo().getInstance(new Girl());
        person.findLove();
    }
}

输出结果

我是媒婆,现在准备开始为你寻找条件合适的相亲对象
高富帅
身高180cm
体重73kg
家里两套房
条件合适的话就准备嘿嘿嘿

通过结果我们可以看到,调用了前置方法、findLove方法、后置方法,但是JDKMeipo中没有findLove方法,person中没有前置后置方法,那么是哪个类调用的呢?

JDK动态代理和CGLIB动态代理浅析

通过debug模式,我们可以看到是一个全新的类$Proxy0,我们生成这个类的字节码文件,同时使用jad将它反编译看看写了什么。

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyTest {
    public static void main(String[] args) {

        Person person = (Person) new JDKMeipo().getInstance(new Girl());
        person.findLove();

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("H://$Proxy0.class");
            fileOutputStream.write(bytes);
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 

import com.gupaoedu.vip.pattern.proxy.service.Person;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements Person
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void findLove()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Person").getMethod("findLove", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

通过看 p r o x y 0 反 编 译 后 的 代 码 我 们 可 以 看 到 , 有 m 1 , m 3 , m 2 , m 0 四 个 方 法 , 这 四 个 方 法 是 通 过 反 射 扫 描 出 来 的 , proxy0反编译后的代码我们可以看到,有m1,m3,m2,m0四个方法,这四个方法是通过反射扫描出来的, proxy0反编译后的代码我们可以看到,有m1,m3,m2,m0四个方法,这四个方法是通过反射扫描出来的,proxy0继承了Proxy类同时实现了Person接口,而m3就是Person类的findLove方法,同时$Proxy0又实现了findLove方法,我们看代码。

  public final void findLove()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(thr	owable);
        }
    }

findLove方法调用了super.h.invoke(this, m3, null);,而这个h是Proxy中的InvocationHandler接口,由于JDKMeipo实现了InvocationHandler接口,因此,super.h.invoke(this, m3, null);实际上调用的就是JDKMeipo的invoke方法。

JDK动态代理小结

实际上JDK动态代理是采用字节重组,重新生成对象来代替原始对象以达到动态代理的目的。JDK Proxy生成对象的步骤如下以媒婆为示例。

  1. 拿到被代理对象(Girl)的引用(Person),并且获取到它所有的接口(本例中的findLove方法),反射获取。(这也是为什么JDK动态代理需要代理类和被代理类都要事先同一接口的原因)
  2. JDK Proxy类重新生成一个新的类,这个新的类需要实现被代理类(Girl)的所有实现的所有接口(findLove)
  3. 动态生成java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
  4. 编译新生成的java代码.class
  5. 再重新加载到JVM中运行

以上这个过程就叫字节码重组。JDK中有一个规范,在ClassPath下只要是$开头的class文件一般都是自动生成的。

动态切换数据源案例改造

前面动态数据源切换案例我们使用的是使用静态代理实现的,我们现在将他改造成动态代理。


/**
 * @Author winston
 * @Date 2021/6/20
 * 将订单数据源切换案例由静态代理改成JDK动态代理
 */
public class JDKOrderProxy implements InvocationHandler {
    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    /**
     * 被代理对象
     */
    private Object proxyObj;

    public Object getInstance(Object proxyObj) {
        // 赋值被代理对象
        this.proxyObj = proxyObj;
        // 获取class信息
        Class<?> clazz = proxyObj.getClass();
        // 返回对象
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object result = method.invoke(proxyObj, args);
        after();
        return result;
    }

    /**
     * 切换数据源
     *
     * @param target 应该是订单对象
     */
    private void before(Object target) {
        try {
            System.out.println("切换数据源");
            // 约定优于配置,所以我们约定存在getCreateTime方法,通过反射得到
            Method method = target.getClass().getMethod("getCreateTime");
            Long time = (Long) method.invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("静态代理类自动分配到【DB_" + dbRouter + "】数据源处理数据。");
            DynamicDataSourceEntry.set(dbRouter);
//            orderService.createOrder(order);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 重置数据源
     *
     */
    private void after() {
        System.out.println("重置数据源");
        DynamicDataSourceEntry.restore();
    }
}

测试代码

/**
 * @author: Winston
 * @createTime: 2021/6/16
 */
public class JdkDynamicProxyOrderTest {
    public static void main(String[] args) throws ParseException {
        Order order = new Order();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2018/02/01");
        order.setCreateTime(date.getTime());
        IOrderService orderService = (IOrderService) new JDKOrderProxy().getInstance(new OrderService());
        orderService.createOrder(order);
    }
}

CGLIB动态代理

参考博客:CGLIB动态代理详解

CGLIB动态代理的结构图如下,我们可以看出代理类去继承目标类,每次调用代理类的方法都会被方法拦截器拦截,在拦截器中才是调用目标类的该方法的逻辑。

JDK动态代理和CGLIB动态代理浅析

首先我们通过代码来感受一下CGLIB动态代理,同样我们以媒婆案例进行改造。

/**
 * @author: Winston
 * @createTime: 2021/6/22
 */
public class CglibMeipoProxy implements MethodInterceptor {

    /**
     * 获取代理对象,这里的参数是Class类型
     * 为啥是class类型,因为这里接收的参数是父类的class,我们需要继承这个父类
     * 重写方法生成新的类
     * @param clazz
     * @return
     */
    public Object getInstance(Class clazz) {
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类
        enhancer.setSuperclass(clazz);
        // 设置拦截器,也就是这个类(CglibMeipoProxy)的intercept方法
        enhancer.setCallback(this);
        // 生成代理类并返回一个实例
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        // 调用被代理方法,注意这里是方法调用并不是用反射
        Object result = methodProxy.invokeSuper(o, objects);
        after();
        return result;
    }


    /**
     * 前置方法
     */
    public void before(){
        System.out.println("我是媒婆,现在准备开始为你寻找条件合适的相亲对象");
    }

    /**
     * 后置方法
     */
    public void after(){
        System.out.println("条件合适的话就准备嘿嘿嘿");
    }
}

测试类

/**
 * @author: Winston
 * @createTime: 2021/6/22
 */
public class CglibMeipoProxyTest {
    public static void main(String[] args) {
        //利用 cglib 的代理类可以将内存中的 class 文件写入本地磁盘
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"H://cglib_proxy_class");
        Boy boy = (Boy) new CglibMeipoProxy().getInstance(Boy.class);
        boy.findLove();
    }

}

运行测试类后我们到指定目录下看一下,发现生成有三个字节码文件,一个是代理类的FastClass(Boy$$EnhancerByCGLIB$$71710d07$$FastClassByCGLIB$$e2a848ac.class),一个是代理类(Boy$$EnhancerByCGLIB$$71710d07.class),一个是目标类的FastClass(Boy$$FastClassByCGLIB$$364eb7e9.class),这里简单说一下什么是FastClass,就是给每个方法编号,通过编号找到方法,这样可以避免频繁使用反射导致效率比较低。

JDK动态代理和CGLIB动态代理浅析

我们通过使用jad工具将代理类的class文件反编译成java文件,我们来简单看一下文件内容。我们可以看到对于eat方法,在这个代理类中对应会有findLoveCGLIB$findLove$0这两个方法;其中前者则是我们使用代理类时候调用的方法,后者是在方法拦截器里面调用的,换句话来说当我们代码调用代理对象的findLove方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIB$findLove$0这个方法。

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   <generated>

package com.gupaoedu.vip.pattern.proxy.service;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;

// Referenced classes of package com.gupaoedu.vip.pattern.proxy.service:
//            Boy

// 这个代理类是继承我们的目标类Boy,并且实现了Factory接口,这个接口就是一些设置回调函数和返回实例化对象的方法
public class Boy$$EnhancerByCGLIB$$71710d07 extends Boy
    implements Factory
{
    static void CGLIB$STATICHOOK1()
    {
        //注意下面这两个Method数组,用于保存反射获取的Method对象,避免每次都用反射去获取Method对象
        Method amethod[];
        Method amethod1[];
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        //获取目标类的字节码文件
        Class class1 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Boy$$EnhancerByCGLIB$$71710d07");
        //代理类的字节码文件
        Class class2;
        //ReflectUtils是一个包装各种反射操作的工具类,通过这个工具类来获取各个方法的Method对象,然后保存到上述的Method数组中
        amethod = ReflectUtils.findMethods(new String[] {
            "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"
        }, (class2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        Method[] _tmp = amethod;
        //为目标类的每一个方法都建立索引,可以想象成记录下来目标类中所有方法的地址,需要用调用目标类方法的时候根据地址就能直接找到该方法
        //这就是此处CGLIB$xxxxxx$$Proxy的作用。。。
        CGLIB$equals$1$Method = amethod[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(class2, class1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = amethod[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = amethod[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(class2, class1, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = amethod[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(class2, class1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        amethod1 = ReflectUtils.findMethods(new String[] {
            "findLove", "()V"
        }, (class2 = Class.forName("com.gupaoedu.vip.pattern.proxy.service.Boy")).getDeclaredMethods());
        Method[] _tmp1 = amethod1;
        CGLIB$findLove$0$Method = amethod1[0];
        CGLIB$findLove$0$Proxy = MethodProxy.create(class2, class1, "()V", "findLove", "CGLIB$findLove$0");
    }
    //这个方法就是调用目标类的的findLove方法
    final void CGLIB$findLove$0()
    {
        super.findLove();
    }

    //这个方法是我们是我们要调用的,在前面的例子中调用代理对象的findLove方法就会到这个方法中
    public final void findLove()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        break MISSING_BLOCK_LABEL_21;
_L4:
        break MISSING_BLOCK_LABEL_37;
        this;
        CGLIB$findLove$0$Method;
        CGLIB$emptyArgs;
        CGLIB$findLove$0$Proxy;
        //这里就是调用方法拦截器的intecept()方法
        intercept();
        return;
        super.findLove();
        return;
    }

    final boolean CGLIB$equals$1(Object obj)
    {
        return super.equals(obj);
    }

    public final boolean equals(Object obj)
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 57;
           goto _L3 _L4
_L3:
        this;
        CGLIB$equals$1$Method;
        new Object[] {
            obj
        };
        CGLIB$equals$1$Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 50;
           goto _L5 _L6
_L5:
        JVM INSTR pop ;
        false;
          goto _L7
_L6:
        (Boolean);
        booleanValue();
_L7:
        return;
_L4:
        return super.equals(obj);
    }

    final String CGLIB$toString$2()
    {
        return super.toString();
    }

    public final String toString()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 40;
           goto _L3 _L4
_L3:
        this;
        CGLIB$toString$2$Method;
        CGLIB$emptyArgs;
        CGLIB$toString$2$Proxy;
        intercept();
        (String);
        return;
_L4:
        return super.toString();
    }

    final int CGLIB$hashCode$3()
    {
        return super.hashCode();
    }

    public final int hashCode()
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 52;
           goto _L3 _L4
_L3:
        this;
        CGLIB$hashCode$3$Method;
        CGLIB$emptyArgs;
        CGLIB$hashCode$3$Proxy;
        intercept();
        JVM INSTR dup ;
        JVM INSTR ifnonnull 45;
           goto _L5 _L6
_L5:
        JVM INSTR pop ;
        0;
          goto _L7
_L6:
        (Number);
        intValue();
_L7:
        return;
_L4:
        return super.hashCode();
    }

    final Object CGLIB$clone$4()
        throws CloneNotSupportedException
    {
        return super.clone();
    }

    protected final Object clone()
        throws CloneNotSupportedException
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        this;
        CGLIB$clone$4$Method;
        CGLIB$emptyArgs;
        CGLIB$clone$4$Proxy;
        intercept();
        return;
_L4:
        return super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature signature)
    {
        String s = signature.toString();
        s;
        s.hashCode();
        JVM INSTR lookupswitch 5: default 120
    //                   -508378822: 60
    //                   1192015562: 72
    //                   1826985398: 84
    //                   1913648695: 96
    //                   1984935277: 108;
           goto _L1 _L2 _L3 _L4 _L5 _L6
_L2:
        "clone()Ljava/lang/Object;";
        equals();
        JVM INSTR ifeq 121;
           goto _L7 _L8
_L8:
        break MISSING_BLOCK_LABEL_121;
_L7:
        return CGLIB$clone$4$Proxy;
_L3:
        "findLove()V";
        equals();
        JVM INSTR ifeq 121;
           goto _L9 _L10
_L10:
        break MISSING_BLOCK_LABEL_121;
_L9:
        return CGLIB$findLove$0$Proxy;
_L4:
        "equals(Ljava/lang/Object;)Z";
        equals();
        JVM INSTR ifeq 121;
           goto _L11 _L12
_L12:
        break MISSING_BLOCK_LABEL_121;
_L11:
        return CGLIB$equals$1$Proxy;
_L5:
        "toString()Ljava/lang/String;";
        equals();
        JVM INSTR ifeq 121;
           goto _L13 _L14
_L14:
        break MISSING_BLOCK_LABEL_121;
_L13:
        return CGLIB$toString$2$Proxy;
_L6:
        "hashCode()I";
        equals();
        JVM INSTR ifeq 121;
           goto _L15 _L16
_L16:
        break MISSING_BLOCK_LABEL_121;
_L15:
        return CGLIB$hashCode$3$Proxy;
_L1:
        JVM INSTR pop ;
        return null;
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback acallback[])
    {
        CGLIB$THREAD_CALLBACKS.set(acallback);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback acallback[])
    {
        CGLIB$STATIC_CALLBACKS = acallback;
    }
    //此方法在静态代码块中被调用
    private static final void CGLIB$BIND_CALLBACKS(Object obj)
    {
        Boy$$EnhancerByCGLIB$$71710d07 boy$$enhancerbycglib$$71710d07 = (Boy$$EnhancerByCGLIB$$71710d07)obj;
        if(boy$$enhancerbycglib$$71710d07.CGLIB$BOUND) goto _L2; else goto _L1
_L1:
        Object obj1;
        boy$$enhancerbycglib$$71710d07.CGLIB$BOUND = true;
        obj1 = CGLIB$THREAD_CALLBACKS.get();
        obj1;
        if(obj1 != null) goto _L4; else goto _L3
_L3:
        JVM INSTR pop ;
        CGLIB$STATIC_CALLBACKS;
        if(CGLIB$STATIC_CALLBACKS != null) goto _L4; else goto _L5
_L5:
        JVM INSTR pop ;
          goto _L2
_L4:
        (Callback[]);
        boy$$enhancerbycglib$$71710d07;
        JVM INSTR swap ;
        0;
        JVM INSTR aaload ;
        (MethodInterceptor);
        CGLIB$CALLBACK_0;
_L2:
    }

    public Object newInstance(Callback acallback[])
    {
        CGLIB$SET_THREAD_CALLBACKS(acallback);
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new Boy$$EnhancerByCGLIB$$71710d07();
    }

    public Object newInstance(Callback callback)
    {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[] {
            callback
        });
        CGLIB$SET_THREAD_CALLBACKS(null);
        return new Boy$$EnhancerByCGLIB$$71710d07();
    }

    public Object newInstance(Class aclass[], Object aobj[], Callback acallback[])
    {
        CGLIB$SET_THREAD_CALLBACKS(acallback);
        JVM INSTR new #2   <Class Boy$$EnhancerByCGLIB$$71710d07>;
        JVM INSTR dup ;
        aclass;
        aclass.length;
        JVM INSTR tableswitch 0 0: default 35
    //                   0 28;
           goto _L1 _L2
_L2:
        JVM INSTR pop ;
        Boy$$EnhancerByCGLIB$$71710d07();
          goto _L3
_L1:
        JVM INSTR pop ;
        throw new IllegalArgumentException("Constructor not found");
_L3:
        CGLIB$SET_THREAD_CALLBACKS(null);
        return;
    }

    public Callback getCallback(int i)
    {
        CGLIB$BIND_CALLBACKS(this);
        this;
        i;
        JVM INSTR tableswitch 0 0: default 30
    //                   0 24;
           goto _L1 _L2
_L2:
        CGLIB$CALLBACK_0;
          goto _L3
_L1:
        JVM INSTR pop ;
        null;
_L3:
        return;
    }

    public void setCallback(int i, Callback callback)
    {
        switch(i)
        {
        case 0: // '\0'
            CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
            break;
        }
    }

    public Callback[] getCallbacks()
    {
        CGLIB$BIND_CALLBACKS(this);
        this;
        return (new Callback[] {
            CGLIB$CALLBACK_0
        });
    }

    public void setCallbacks(Callback acallback[])
    {
        this;
        acallback;
        JVM INSTR dup2 ;
        0;
        JVM INSTR aaload ;
        (MethodInterceptor);
        CGLIB$CALLBACK_0;
    }
    //这里有很多的属性,仔细看一下就是一个方法对应两个,一个是Method类型,一个是MethodProxy类型
    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$findLove$0$Method;
    private static final MethodProxy CGLIB$findLove$0$Proxy;
    private static final Object CGLIB$emptyArgs[];
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;
    //静态代码块,调用下面静态方法,这个静态方法大概做的就是获取目标方法中每个方法的MethodProxy对象
    static 
    {
        CGLIB$STATICHOOK1();
    }
    //无参构造器
    public Boy$$EnhancerByCGLIB$$71710d07()
    {
        CGLIB$BIND_CALLBACKS(this);
    }
}

FastClass机制分析

为什么要用这种机制呢?直接用反射多好啊,但是我们知道反射虽然很好用,但是和直接new对象相比,效率有点慢,于是就有了这种机制,参考这篇博客https://www.cnblogs.com/cruze/p/3865180.html,有个小例子可以说的非常清楚;

public class test10 {  //这里,tt可以看作目标对象,fc可以看作是代理对象;首先根据代理对象的getIndex方法获取目标方法的索引,  //然后再调用代理对象的invoke方法就可以直接调用目标类的方法,避免了反射
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null);
    }
}

class Test{
    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){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    //这个方法对Test类中的方法建立索引
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }
}

CGLIB动态代理小结

上面我们看了CGLib动态代理的用法、实际生成的代理类以及FastClass机制,下面我们就以最前面的那个例子中调用findLove()方法来看看主要的调用步骤;

第一步:是经过一系列操作实例化出了Enhance对象,并设置了所需要的参数然后enhancer.create()成功创建出来了代理对象,这个就不多说了…

第二步:调用代理对象的findLove()方法,会进入到方法拦截器的intercept()方法,在这个方法中会调用proxy.invokeSuper(obj, args);方法

第三步:invokeSuper中,通过FastClass机制调用目标类的方法

CGLIB动态代理和JDK动态代理比较

  1. JDK动态代理是实现了被代理对象的接口,CGLIB动态代理是继承了被代理对象
  2. JDK和CGLIB都是在运行时生成字节码,JDK是直接生成Class字节码,CGLIB是使用ASM框架写Class字节码,CGLIB代理实现更复杂,因此生成代理类比JDK效率低
  3. JDK调用代理方法是通过反射机制调用,而CGLIB是通过FastClass机制直接调用方法,因此CGLIB执行效率更高。

Spring 中的代理选择的原则

  1. 当Bean有实现接口时,Spring就会使用JDK的动态代理
  2. 当Bean没有实现接口时,Spring使用Cglib动态代理
  3. Spring可以强制使用Cglib动态代理,只需要在Spring的配置文件中加如下代码
<aop:aspectj-autoproxy proxy-target-class="true"/>
上一篇:JDK和CGLib动态代理实现和区别


下一篇:代理