首页 > 编程语言 >【代理】【六】代理源码解析-Cglib代理-代理文件执行分析

【代理】【六】代理源码解析-Cglib代理-代理文件执行分析

时间:2023-03-04 20:33:41浏览次数:48  
标签:var1 CGLIB var10000 Object 代理 say 源码 Cglib

1  前言

上节我们简单看了 Enhancer 的 generateClass 和 firstInstance,两个方法的内容也就是创建代理以及生成代理对象的内容,这节我们结合实际生成的文件了解一下代理的实际执行过程。

2  代理文件分析

我们看一下生成的文件有三个:代理类以及两个FastClass,那我们来看下代理文件的内容。

2.1  代理类

我们首先来看下代理类的内容,我里边去掉一些默认的方法哦比如 equals、toString啥的,只看我们的关键方法哈:

public class UserServiceImpl$$EnhancerByCGLIB$$db94c8d6 extends UserServiceImpl implements Factory {
    // 变量
    private boolean CGLIB$BOUND;
    // 本地线程变量
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    // 我们的增强逻辑回调
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    // 增强逻辑
    private MethodInterceptor CGLIB$CALLBACK_0;
    /**
     * $Method 结尾的就是我们的正常方法比如 say
     * $Proxy  结尾的是生成方法的代理方法 比如 say的代理方式是 CGLIB$say$0$Proxy
     *         是通过MethodProxy创建的 用于串联增强逻辑
     */
    private static final Method CGLIB$say$0$Method;
    private static final MethodProxy CGLIB$say$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$say2$1$Method;
    private static final MethodProxy CGLIB$say2$1$Proxy;
    private static final Method CGLIB$equals$3$Method;
    private static final MethodProxy CGLIB$equals$3$Proxy;

    // 静态代码块
    static {
        // 就是下边的这个方法
        CGLIB$STATICHOOK1();
    }

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        // 记住这里的 var0 是 代理类, var1 是 被代理类 UserServiceImpl
        Class var0 = Class.forName("net.test.UserServiceImpl$$EnhancerByCGLIB$$db94c8d6");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"say", "()V", "say2", "()V"}, 
(var1 = Class.forName("net.test.UserServiceImpl")).getDeclaredMethods()); CGLIB$say$0$Method = var10000[0]; CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0"); CGLIB$say2$1$Method = var10000[1]; CGLIB$say2$1$Proxy = MethodProxy.create(var1, var0, "()V", "say2", "CGLIB$say2$1"); var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString",
"()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods()); CGLIB$equals$3$Method = var10000[1]; CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3"); } // 空参构造 public UserServiceImpl$$EnhancerByCGLIB$$db94c8d6() { // 调用下边这个方法 CGLIB$BIND_CALLBACKS(this); } // var0 就是代理对象本身 private static final void CGLIB$BIND_CALLBACKS(Object var0) { UserServiceImpl$$EnhancerByCGLIB$$db94c8d6 var1 = (UserServiceImpl$$EnhancerByCGLIB$$db94c8d6)var0; // CGLIB$BOUND 第一次进来为 false if (!var1.CGLIB$BOUND) { // 再设置为 true var1.CGLIB$BOUND = true; /** * 从本地线程中获取增强逻辑,没有的话就用类信息里的 * 也就是我们上节 firstInstance 方法会塞进来我们的增加逻辑的哪个类也就是 MyMethodInterceptor */ Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { // 本地线程中没有的就用类信息里的 var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null) { return; } } // 设置进 CGLIB$CALLBACK_0 var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0]; } } final void CGLIB$say$0() { super.say(); } public final void say() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy); } else { super.say(); } } }

可以看到代理对象静态代码块,就会创建每个方法的代理方法出来,并设置上增强逻辑的回调,那么我么你先看下方法代理的创建:

/**
 * MethodProxy 
 * @param c1 被代理类 UserServiceImpl
 * @param c2 代理类
 * @param desc 方法描述 比如 ()V (Ljava/lang/Object;)Z
 * @param name1 正常的方法名 比如 say  这个会走增强逻辑
 * @param name2 代理的方法名 比如 CGLIB$say$0 但是你看它生成的类 这个方法反而直接简单是调用父类的 也就是说不会走增强逻辑
 * @return
 */
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 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)
        {
            // 被代理类 UserServiceImpl
            this.c1 = c1;
            // 代理类
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if (fromEnhancer != null) {
                namingPolicy = fromEnhancer.getNamingPolicy();
                strategy = fromEnhancer.getStrategy();
                attemptLoad = fromEnhancer.getAttemptLoad();
            }
        }
    }

一定分析清楚我们的c1、c2 表示的啥哈。

2.2  FastClass

两个 FastClass我们只看其中一个哈,因为生成的方法都是一样的,内容上格式都差不多:

package net.test;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

public class UserServiceImpl$$FastClassByCGLIB$$65337df extends FastClass {
    public UserServiceImpl$$FastClassByCGLIB$$65337df(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {case -909388886:
            if (var10000.equals("say()V")) {
                return 0;
            }
            break;case 1874011246:
            if (var10000.equals("say2()V")) {
                return 1;
            }
            break;return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {case 113643:
            if (var1.equals("say")) {
                switch(var2.length) {
                case 0:
                    return 0;
                }
            }
            break;
        case 3522983:
            if (var1.equals("say2")) {
                switch(var2.length) {
                case 0:
                    return 1;
                }
            }
            break;
    return -1;
    }

    public int getIndex(Class[] var1) {
        switch(var1.length) {
        case 0:
            return 0;
        default:
            return -1;
        }
    }

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserServiceImpl var10000 = (UserServiceImpl)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.say();
                return null;
            case 1:
                var10000.say2();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }public int getMaxIndex() {
        return 10;
    }
}

3  方法的实际执行过程

看过了cglib代理产生的东西以后,我有一个困惑的就是 FastClass 是什么时候发挥作用的呢,还没看到是不是,那么我们从一个实际的方法调用我们看下执行的过程:

public static void main(String[] args) {
    // 设置代理文件输出位置
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./code");
    // 创建 cglib 代理类的核心对象
    Enhancer enhancer = new Enhancer();
    // 设置要创建代理的类
    enhancer.setSuperclass(UserServiceImpl.class);
    // 跟 JDK 代理一样,增强的逻辑
    enhancer.setCallback(new MyMethodInterceptor());
    // 创建代理,并调用
    UserServiceImpl userService = (UserServiceImpl) enhancer.create();
    userService.say();
}

也就是调用我们的 say 方法后执行开始:

public final void say() {
    // 我们的增强逻辑
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    /**
     * Enhancer 里的 firstInstance的方法在创建代理对象的时候已经初始化上了
     * 所以这里不空
     */
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }
    // 不空执行代理我们的增强逻辑 也就是我们的 MyMethodInterceptor
    if (var10000 != null) {
        var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
    } else {
        super.say();
    }
}

然后我们看下我们的增强逻辑:

/**
 * 
 * @param o  this 也就是我们的代理对象
 * @param method intercepted Method  say方法
 * @param objects   方法参数
 * @param methodProxy  代理类的代理方法 也就是MethodProxy类型的 CGLIB$say$0$Proxy 
 * @return
 * @throws Throwable
 */
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("我是执行前");
    Object result = methodProxy.invokeSuper(o, objects);
    System.out.println("我是执行后");
    return result;
}

继续我们看下 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();
    }
}
private void init() {
    if (fastClassInfo == null)
    {
        synchronized (initLock)
        {
            if (fastClassInfo == null)
            {
                CreateInfo ci = createInfo;
                FastClassInfo fci = new FastClassInfo();
                // f1 被代理类
                fci.f1 = helper(ci, ci.c1);
                // f2 代理类
                fci.f2 = helper(ci, ci.c2);
                // 被代理类的FastClass say 方法的索引
                fci.i1 = fci.f1.getIndex(sig1);
                // 代理类的FastClass CGLIB$say$0 方法的索引
                fci.i2 = fci.f2.getIndex(sig2);
                fastClassInfo = fci;
            }
        }
    }
}

我们看到最后调的是 fci.f2.invoke 也就是:

是不是这样就完事?不知道你有没有晕掉。

3.1  问题一:我这里不调用invokeSuper 调用 invoke 会发生什么呢?

首先我们要知道 invoke里内容是什么我们来看下:

可以看到 invokeSuper是调用的f2.invoke,invoke是调用的f1.invoke 也就是调用say 方法是不是? 那么我们当前的对象是代理对象,代理对象的say方法又是什么呢?

 

看就是还是会走增强逻辑也就是说进入一个死循环了,是不是就会栈溢出了。

3.2  问题二:那我不调用MethodProxy的了,我直接调用Method呢?

我直接说答案了奥,就不贴图了哈,其实还是会死循环,因为Method就还是 say方法,一定注意的是我们当前的对象就是 代理对象,代理对象的 say方法里是走增强逻辑的,所以还是会死循环。

4  小结

这节我们看了下代理实际的执行过程,也看到了 FastClass的调用时机就是 执行我们的代理方法的invoke时,init方法里会初始化FastClass,然后获取到当前方法的索引,继而进行调用。有理解不对的地方欢迎指正哈。

标签:var1,CGLIB,var10000,Object,代理,say,源码,Cglib
From: https://www.cnblogs.com/kukuxjx/p/17178849.html

相关文章

  • 源码编译 - hadoop3.31
    hadoop源码编译一、准备工作1、下载hadoop源码官网地址:https://www.apache.org/index.html#projects-list清华大学镜像网站:https://mirrors.tuna.tsinghua.edu.cn/apa......
  • IPIDEA教你怎么选择适合TikTok的代理IP?
    要说到现在最火的自媒体平台,不得不提的就是短视频流量霸主TikTok,无论是国内版的抖音,还是国际版的TikTok,都是新一代年轻人中的流行趋势。不管是个体还是企业,都希望能抓住Ti......
  • 重新编译harbor-exporter源码,构建镜像
    1.githubhttps://github.com/c4po/harbor_exporter 2.dockerfileFROMgolang:1.17ENVGO111MODULE=on\GOPROXY="https://goproxy.cn,direct"COPYharbor_exp......
  • Java集合LinkedList源码中 实现 List 接口 却没有 在 LinkedList实现全部的 List接口
    Java集合LinkedList源码中实现List接口却没有在LinkedList实现全部的List接口方法普通类实现接口,应该实现接口中全部的抽象方法。难道是源码实现接口有什么特殊的......
  • 【Spring Boot源码剖析之Spring Boot源码剖析】
    SpringBoot源码剖析SpringBoot依赖管理问题:(1)为什么导入dependency时不需要指定版本?SpringBoot项目的父项目依赖spring-boot-starter-parent<parent><groupId>org.......
  • 【代理】【五】代理源码解析-Cglib代理-Enhancer代理对象创建过程
    1 前言上节我们看了EnhancerKey是通过KeyFactory里的 Generator来创建代理对象,这节我们就来看下Enhancer是如何创建代理对象的。2 源码分析上节我们看过了,Abst......
  • 反射与泛型、动态代理
    泛型在Java中的泛型简单来说就是:在创建对象或调⽤⽅法的时候才明确下具体的类型好处是:代码更加简洁(不再需要强制转换),程序更加健壮(在编译期间没有警告,在运⾏期就不会出现......
  • QT的Proxy Model(代理模型)
    在Qt中,ProxyModel(代理模型)是一种机制,可以让我们在不修改源数据的情况下,对数据进行排序、筛选、修改、隐藏、转换等操作。ProxyModel实际上是一种中间层,它将来自源模型的......
  • 【代理】【四】代理源码解析-Cglib代理-Generator代理对象创建过程
    1 前言 上节我们简单感受了下Cglib的一个代理过程,并且我也说到了Enhancer创建代理的时候,首先会创建对象Key出来,用于标识这个类以及将其作为缓存的Key,然后再创建代理对......
  • Apache设置反向代理解决js跨域问题
    这是一个很简单的方案,通过启用Apache反向代理解决js跨域问题为什么要这么做?在现在的开发过程中大家会遇到这样一个问题:后端代码写好之后,前端的小伙伴需要将后端代码部署......