[Java] 从 class 文件看 cglib 对 MethodInterceptor 的处理 (下)

背景

[Java] 从 class 文件看 cglib 对 MethodInterceptor 的处理 (上) 一文中,我们已经初步探讨了下方的问题,本文会继续探讨这个问题。

  • 为什么 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.proxy.MethodInterceptor \text{net.sf.cglib.proxy.MethodInterceptor} </math>net.sf.cglib.proxy.MethodInterceptor 可以调用动态代理类的基类(即例子中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 类)中的方法
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass 会为 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 中定义的非私有方法分配编号(index)
    • 通过使用这个编号(index),我们可以调用 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 中对应的方法

要点

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Adder \text{Adder} </math>Adder 类中定义了 <math xmlns="http://www.w3.org/1998/Math/MathML"> a d d ( i n t , i n t ) add(int, int) </math>add(int,int) 方法
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> cglib \text{cglib} </math>cglib 为 <math xmlns="http://www.w3.org/1998/Math/MathML"> Adder \text{Adder} </math>Adder 类生成的子类(简称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> Adder enhancer \text{Adder}_\text{enhancer} </math>Adderenhancer)中会有以下两个方法
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> CGLIBadd0(int, int) \text{CGLIB{\textdollar}add{\textdollar}0(int, int)} </math>CGLIBadd0(int, int): 它的逻辑是调用 基类 中的 <math xmlns="http://www.w3.org/1998/Math/MathML"> add(int, int) \text{add(int, int)} </math>add(int, int) 方法并返回对应的值
    • <math xmlns="http://www.w3.org/1998/Math/MathML"> add(int, int) \text{add(int, int)} </math>add(int, int)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> Adder \text{Adder} </math>Adder 类的子类 <math xmlns="http://www.w3.org/1998/Math/MathML"> Adder e n h a n c e r \text{Adder}_{enhancer} </math>Adderenhancer 中有 <math xmlns="http://www.w3.org/1998/Math/MathML"> MethodProxy \text{MethodProxy} </math>MethodProxy 类型的 <math xmlns="http://www.w3.org/1998/Math/MathML"> CGLIBadd0Proxy \\text{CGLIB\\textdollar add\\textdollar 0\\textdollar Proxy} CGLIBadd0Proxy 字段
  • 对 <math xmlns="http://www.w3.org/1998/Math/MathML"> CGLIBadd0Proxy \\text{CGLIB\\textdollar add\\textdollar 0\\textdollar Proxy} CGLIBadd0Proxy 的 <math xmlns="http://www.w3.org/1998/Math/MathML"> invokeSuper(Object obj, Object[] args) \text{invokeSuper(Object obj, Object[] args)} </math>invokeSuper(Object obj, Object[] args) 方法的调用,最终会转化为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ( Adder enhancer ) o b j ) . CGLIBadd0 ( ( ( Number ) args[0] ) . intValue() , ( ( Number ) args[1] ) . intValue() ) ((\text{Adder}_\text{enhancer}) obj).\text{CGLIB{\textdollar}add{\textdollar}0}(((\text{Number})\text{args[0]}).\text{intValue()}, ((\text{Number})\text{args[1]}).\text{intValue()}) </math>((Adderenhancer)obj).CGLIBadd0(((Number)args[0]).intValue(),((Number)args[1]).intValue()) 这样的逻辑
  • 对 <math xmlns="http://www.w3.org/1998/Math/MathML"> CGLIBadd0Proxy \\text{CGLIB\\textdollar add\\textdollar 0\\textdollar Proxy} CGLIBadd0Proxy 的 invoke(Object obj, Object[] args) 方法的调用,最终会转化为 ((Adder) obj).add(((Number)args[0]).intValue(), ((Number)args[1]).intValue()) 这样的逻辑

正文

项目结构

项目结构和 [Java] 从 class 文件看 cglib 对 MethodInterceptor 的处理 (上) 一文相同,可以参考那篇文章里的 项目结构 这一小节。

分析

两个 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass

[Java] 如何通过 cglib 的 FastClass 调用一个类中的"任意"方法? 一文中,我们已经了解到,对一个类 <math xmlns="http://www.w3.org/1998/Math/MathML"> C \text{C} </math>C 而言,借助对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass : FastClass C \text{net.sf.cglib.reflect.FastClass}: \text{FastClass}_\text{C} </math>net.sf.cglib.reflect.FastClass:FastClassC,我们可以(不通过反射 )调用 <math xmlns="http://www.w3.org/1998/Math/MathML"> C \text{C} </math>C 中定义的任意非私有方法。

那么如果我们既有和 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass(可以简称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 1 \text{FastClass}_1 </math>FastClass1),又有和 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 的子类对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass(可以简称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 2 \text{FastClass}_2 </math>FastClass2),那么就可以自由调用我们在 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 中定义的方法了 ⬇️

类名 对应的 FastClass 是什么
Adder Adder$$FastClassByCGLIB$$2ea8c1e0
Adder$$EnhancerByCGLIB$$82ff904 (Adder 的子类) Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd

这两个 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass 子类的名称太长了,为了便于描述,我们把它们分别简称为 ⬇️

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 1 \text{FastClass}_1 </math>FastClass1 (和 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 对应)
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 2 \text{FastClass}_2 </math>FastClass2 (和 <math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 的子类对应)

我们分别看看对应的代码是什么样子的

Adder 对应的 FastClass: <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 1 \text{FastClass}_1 </math>FastClass1

<math xmlns="http://www.w3.org/1998/Math/MathML"> org.example.Adder \text{org.example.Adder} </math>org.example.Adder 对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass 是 Adder$$FastClassByCGLIB$$2ea8c1e0 (简称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 1 \text{FastClass}_1 </math>FastClass1),借助 IntelliJ IDEA (Community Edition) 可以看到 Adder$$FastClassByCGLIB$$2ea8c1e0.class 反编译的结果。完整的结果比较长,与本文相关的内容如下(不相关的内容用 ... 表示)

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example;

...

public class Adder$$FastClassByCGLIB$$2ea8c1e0 extends FastClass {
    ...

    public int getIndex(String var1, Class[] var2) {
        switch (var1.hashCode()) {
            ...
            case 96417:
                if (var1.equals("add")) {
                    switch (var2.length) {
                        case 2:
                            if (var2[0].getName().equals("int") && var2[1].getName().equals("int")) {
                                return 0;
                            }
                    }
                }
                break;
            ...
    }

    ...

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

        try {
            switch (var10001) {
                case 0:
                    return new Integer(var10000.add(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                ...
            }
        } 
        ...
    }

    ...
}
Adder 的子类对应的 FastClass: <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 2 \text{FastClass}_2 </math>FastClass2

Adder$$EnhancerByCGLIB$$82ff904Adder 的子类,前者对应的 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.reflect.FastClass \text{net.sf.cglib.reflect.FastClass} </math>net.sf.cglib.reflect.FastClass 是 Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd(简称为 <math xmlns="http://www.w3.org/1998/Math/MathML"> FastClass 2 \text{FastClass}_2 </math>FastClass2),借助 IntelliJ IDEA (Community Edition) 可以看到 Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd.class 反编译的结果。完整的结果比较长,与本文相关的内容如下(不相关的内容用 ... 表示)

(在您的电脑上,Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd.class 的内容可能会有差异)

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example;

...

public class Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd extends FastClass {
    ...

    public int getIndex(String var1, Class[] var2) {
        switch (var1.hashCode()) {
            ...
            case 96417:
                if (var1.equals("add")) {
                    switch (var2.length) {
                        case 2:
                            if (var2[0].getName().equals("int") && var2[1].getName().equals("int")) {
                                return 4;
                            }
                    }
                }
                break;
            ...
            case 1108311562:
                if (var1.equals("CGLIB$add$0")) {
                    switch (var2.length) {
                        case 2:
                            if (var2[0].getName().equals("int") && var2[1].getName().equals("int")) {
                                return 17;
                            }
                    }
                }
                break;
            ...
    }
    
    ...

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        // 由于反编译的结果对 '$' 的处理有点 bug,我手动调整了下一行的内容
        Adder$$EnhancerByCGLIB$$82ff904 var10000 = (Adder$$EnhancerByCGLIB$$82ff904)var2;
        int var10001 = var1;

        try {
            switch (var10001) {
                ...
                case 4:
                    return new Integer(var10000.add(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                ...
                case 17:
                    return new Integer(var10000.CGLIB$add$0(((Number)var3[0]).intValue(), ((Number)var3[1]).intValue()));
                ...
            }
        }
        ...
    }

    ...
}

AdderFactory 中的 methodInterceptor

AdderFactory 中的 methodInterceptor 字段对应的代码如下

java 复制代码
private static final MethodInterceptor methodInterceptor =
        (obj, method, args, proxy) -> {
            if (method.getName().equals("add")) {
                String message = String.format("The arguments for the add method are: %s",
                        Arrays.toString(args));
                System.out.println(message);
            }
            return proxy.invokeSuper(obj, args);
        };

当我们运行 AdderFactoryTest 中的 testBuildAdder 方法时,adder 会是 Adder$$EnhancerByCGLIB$$82ff904 的实例 ⬇️

Adder$$EnhancerByCGLIB$$82ff904add(int, int) 方法反编译的结果如下

java 复制代码
public final int add(int var1, int var2) {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {
        // 为了让代码便于阅读,我调整了下方代码的缩进风格
        Object var3 = var10000.intercept(
            this, 
            CGLIB$add$0$Method, 
            new Object[]{new Integer(var1), new Integer(var2)},
            CGLIB$add$0$Proxy
        );
        return var3 == null ? 0 : ((Number)var3).intValue();
    } else {
        return super.add(var1, var2);
    }
}

其中 CGLIB$add$0$Proxy 是 <math xmlns="http://www.w3.org/1998/Math/MathML"> net.sf.cglib.proxy.MethodProxy \text{net.sf.cglib.proxy.MethodProxy} </math>net.sf.cglib.proxy.MethodProxy 的实例

其他

画 "对 FastClass 的使用" 一图所用到的代码

puml 复制代码
@startuml
'https://plantuml.com/class-diagram

title 对 <i>FastClass</i> 的使用
caption 图中只画了本文关心的内容\n<i>FastClass</i> 和它的子类, 在图中用 <b>粉色</b> 标出来了

class org.example.Adder
class org.example.Adder$$EnhancerByCGLIB$$82ff904

org.example.Adder <|-- org.example.Adder$$EnhancerByCGLIB$$82ff904

class org.example.Adder {
    + int add(int a, int b)
}

class org.example.Adder$$EnhancerByCGLIB$$82ff904 {
    - MethodInterceptor CGLIB$CALLBACK_0
    - {static} final MethodProxy CGLIB$add$0$Proxy
    + final int add(int, int)
    final int CGLIB$add$0(int, int)
}

abstract class net.sf.cglib.reflect.FastClass #pink {
    + {abstract} int getIndex(String name, Class[] parameterTypes)
    + {abstract} Object invoke(int index, Object obj, Object[] args) throws InvocationTargetException
}

class org.example.Adder$$FastClassByCGLIB$$2ea8c1e0
net.sf.cglib.reflect.FastClass <|-- org.example.Adder$$FastClassByCGLIB$$2ea8c1e0

class org.example.Adder$$FastClassByCGLIB$$2ea8c1e0 #pink {
    + int getIndex(String, Class[])
    + Object invoke(int, Object, Object[]) throws InvocationTargetException
}

note top of org.example.Adder$$FastClassByCGLIB$$2ea8c1e0
它为 <i>org.example.Adder</i> 中的
一些方法分配了编号 (<i>index</i>) <:point_down:>
| <b>方法</b> | <b>对应的 <i>index</i></b> |
| <b><i>add(int, int)</i></b> | <b><i>0</i></b> |
|...|...|
end note

class org.example.Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd
net.sf.cglib.reflect.FastClass <|-- org.example.Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd
class org.example.Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd #pink {
    + int getIndex(String, Class[])
    + Object invoke(int, Object, Object[]) throws InvocationTargetException
}

note top of org.example.Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd
它为 <i>org.example.Adder</i> 的 <b>子类</b> 中的
一些方法分配了编号 (<i>index</i>) <:point_down:>
(在您的电脑上, 对应的 <i>index</i> 也许会有差异)
| <b>方法</b> | <b>对应的 <i>index</i></b> |
|...|...|
| <b><i>add(int, int)</i></b> | <b><i>4</i></b> |
|...|...|
| <b><i>CGLIB$add$0(int, int)</i></b> | <b><i>17</i></b> |
|...|...|
end note

org.example.Adder <.. org.example.Adder$$FastClassByCGLIB$$2ea8c1e0 : 和 <b><i>org.example.Adder</i></b> 对应
org.example.Adder$$EnhancerByCGLIB$$82ff904 <.. org.example.Adder$$EnhancerByCGLIB$$82ff904$$FastClassByCGLIB$$8d93dfdd : 和 <b><i>org.example.Adder$$EnhancerByCGLIB$$82ff904</i></b> 对应
@enduml

画 "MethodProxy 中的 FastClassInfo" 一图所用到的代码

puml 复制代码
@startuml

title <i>MethodProxy</i> 中的 <i>FastClassInfo</i>
caption 图中只画了本文关心的内容

class net.sf.cglib.proxy.MethodProxy {
    - volatile FastClassInfo fastClassInfo
}

class net.sf.cglib.proxy.MethodProxy$FastClassInfo {
    FastClass f1
    FastClass f2
    int i1
    int i2
}

object o {
    f1: <i>FastClass<sub>1</sub></i> 的实例
    f2: <i>FastClass<sub>2</sub></i> 的实例
    i1: 0 (和 <i>Adder</i> 类中的 <b><i>add(int, int)</i></b> 方法对应的 <b><i>index</i></b> 的值)
    i2: 17 (和 <i>Adder</i> 类子类中的 <b><i>CGLIB$add$0(int, int)</i></b> 方法对应的 <b><i>index</i></b> 的值)
}

o ..> net.sf.cglib.proxy.MethodProxy$FastClassInfo: <i>o</i> 是 <i>net.sf.cglib.proxy.MethodProxy$FastClassInfo</i> 的实例

note as n1
<i>o</i> 和 <b><i>Adder$$EnhancerByCGLIB$$82ff904</i></b> 类的
<b><i>CGLIB$add$0$Proxy</i></b> 字段持有相同的引用
end note

o .. n1

@enduml
相关推荐
lee_curry1 小时前
Java中关于“锁”的那些事
java·线程·并发·juc
pearlthriving1 小时前
c++当中的泛型思想以及c++11部分新特性
java·开发语言·c++
Walter先生2 小时前
WebSocket 连接池生产级实现:实时行情高可用与负载均衡
后端·websocket·架构
梦魇星虹2 小时前
idea Cannot find declaration to go to
java·ide·intellij-idea
小雅痞2 小时前
[Java][Leetcode hard] 42. 接雨水
java·开发语言·leetcode
xfcoding2 小时前
关于代码注释的思考
java
虹梦未来2 小时前
【开发心得】在SpringBoot体系中正确使用redisConfig
java·spring boot·spring
skiy2 小时前
Spring Framework 中文官方文档
java·后端·spring
jserTang2 小时前
Claude Code 源码深度解析 - 前言
前端·javascript·后端