背景
使用 cglib 时,会用到 的子接口 ⬇️
它们的简要类图如下 ⬇️
在使用 时,cglib 所生成的动态代理类是怎样的呢?让我们一起探索吧。
注意:本文所提到的 都是 (而非 JDK 中的 )
要点
代码
铺垫
假设有一个逻辑门的接口 ⬇️
public interface LogicGate {
boolean calculate(boolean... input);
}
我们希望实现 与门。一种可行的方法是使用 来实现 与门 的逻辑。
项目结构
我们在项目顶层执行 tree . 命令,会看到如下的结果 ⬇️
.
├── pom.xml
└── src
├── main
│ └── java
│ └── org
│ └── example
│ ├── GateFactory.java
│ └── LogicGate.java
└── test
└── java
└── org
└── example
└── GateFactoryTest.java
10 directories, 4 files
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>cglib-study</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.5</version> <!-- Use a recent version -->
<configuration>
<argLine>--add-opens=java.base/java.lang=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
LogicGate.java
package org.example;
public interface LogicGate {
boolean calculate(boolean... input);
}
GateFactory.java
package org.example;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;
public class GateFactory {
static {
// 将 cglib 生成的类保存到当前目录下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
}
private static final InvocationHandler invocationHandler = new InvocationHandler() {
private void validateLength(boolean[] input) {
if (input.length == 0) {
throw new IllegalArgumentException("And gate should have at least one input!");
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
boolean[] input = (boolean[]) args[0];
validateLength(input);
for (boolean item : input) {
if (!item) {
return false;
}
}
return true;
}
};
public static LogicGate buildAndGate() {
return (LogicGate) Enhancer.create(LogicGate.class, invocationHandler);
}
}
GateFactoryTest.java
package org.example;
import net.sf.cglib.proxy.InvocationHandler;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
public class GateFactoryTest {
private final LogicGate andGate = GateFactory.buildAndGate();
@Test
public void testBuildAndGate() {
Assert.assertTrue(andGate.calculate(true, true));
Assert.assertFalse(andGate.calculate(true, false));
Assert.assertFalse(andGate.calculate(false, false));
Assert.assertFalse(andGate.calculate(false, false));
}
@Test
public void testInvocationHandler() throws ReflectiveOperationException {
Field invocationHandlerField = GateFactory.class.getDeclaredField("invocationHandler");
invocationHandlerField.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) invocationHandlerField.get(null);
Field cglibCallback0Field = andGate.getClass().getDeclaredField("CGLIB$CALLBACK_0");
cglibCallback0Field.setAccessible(true);
InvocationHandler cglibCallback0 = (InvocationHandler) cglibCallback0Field.get(andGate);
Assert.assertSame(invocationHandler, cglibCallback0);
}
}
LogicGate/GateFactory/GateFactoryTest 这三个类的类图如下 ⬇️
运行
在项目顶层执行如下命令,可以运行单元测试
mvn clean test
运行后,会看到项目顶层多了 net 和 org 这两个目录。执行 tree net org 命令后,会看到如下结果 ⬇️
net
└── sf
└── cglib
├── core
│ └── MethodWrapper$MethodWrapperKey$$KeyFactoryByCGLIB$$d45e49f7.class
└── proxy
└── Enhancer$EnhancerKey$$KeyFactoryByCGLIB$$7fb24d72.class
org
└── example
└── LogicGate$$EnhancerByCGLIB$$580215af.class
7 directories, 3 files
LogicGate$$EnhancerByCGLIB$$580215af.class 看起来和 LogicGate 直接相关。我们在 Intellij IDEA (Community Edition) 可以看到前者反编译的结果(完整的结果比较长,这里就不展示了)。下方是 LogicGate$$EnhancerByCGLIB$$580215af 的类图
分析
我们来看看 LogicGate$$EnhancerByCGLIB$$580215af 中的 calculate(boolean... var1) 方法 ⬇️
public final boolean calculate(boolean... var1) {
try {
InvocationHandler var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return (Boolean)var10000.invoke(this, CGLIB$calculate$4, new Object[]{var1});
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
看起来主线逻辑是这样的 ⬇️
以下两者是否为同一个引用呢?
LogicGate$$EnhancerByCGLIB$$580215af中的CGLIB$CALLBACK_0字段GateFactory中的invocationHandler字段
我用如下的单元测试(上文已提供完整代码)验证了一下 ⬇️ 两者确实为同一个引用
其他
画 "net.sf.cglib.proxy.Callback 和它的子接口" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
title <i>net.sf.cglib.proxy.Callback</i> 和它的子接口
interface net.sf.cglib.proxy.Callback
interface net.sf.cglib.proxy.MethodInterceptor
interface net.sf.cglib.proxy.NoOp
interface net.sf.cglib.proxy.LazyLoader
interface net.sf.cglib.proxy.Dispatcher
interface net.sf.cglib.proxy.InvocationHandler
interface net.sf.cglib.proxy.FixedValue
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.MethodInterceptor
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.NoOp
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.LazyLoader
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.Dispatcher
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.InvocationHandler
net.sf.cglib.proxy.Callback <|-- net.sf.cglib.proxy.FixedValue
note top of net.sf.cglib.proxy.Callback
这是一个 <i>marker interface</i>
(这个接口里没有定义任何方法)
end note
@enduml
画 "主要步骤" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startwbs
* 主要步骤
**: <i>cglib</i> 生成动态代理类
(为便于叙述, 将这个代理类简称为 <b><i>P</i></b>);
***:在代理类 <b><i>P</i></b> 中,
<i>CGLIB$CALLBACK_0</i> 字段保存
我们提供的 <i>net.sf.cglib.proxy.InvocationHandler</i> 实例
(后者简称为 <b><i>ih</i></b>);
**:在调用代理类 <b><i>P</i></b> 的 <i>equals(Object)/toString()/hashCode()</i> 等方法时
可以通过 <i>CGLIB$CALLBACK_0</i> 字段
让 <b><i>ih</i></b> 接管后续的处理逻辑;
@endwbs
画 "cglib 生成的动态代理类的实例可以访问 ih" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
'https://plantuml.com/object-diagram
title <i>cglib</i> 生成的动态代理类的实例可以访问 <i>ih</i>
caption \n\n
' caption 的内容是为了防止掘金平台生成的水印遮盖图中的文字
object ih {
}
object p {
CGLIB$CALLBACK_0
}
p::CGLIB$CALLBACK_0 --> ih
note bottom of p
<i>p</i> 是 <i>cglib</i> 生成的动态代理类的实例
end note
note bottom of ih
<i>ih</i> 是我们提供的 <i>net.sf.cglib.proxy.InvocationHandler</i> 实例
end note
@enduml
画 "三个类的的类图" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
'https://plantuml.com/class-diagram
title 三个类的的类图
interface org.example.LogicGate {
boolean calculate(boolean...)
}
class org.example.GateFactory {
- {static} final net.sf.cglib.proxy.InvocationHandler invocationHandler
+ {static} LogicGate buildAndGate()
}
class org.example.GateFactoryTest {
- final LogicGate andGate
+ void testBuildAndGate()
+ void testInvocationHandler()
}
@enduml
画 "org.example.LogicGate580215af 的类图" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
'https://plantuml.com/class-diagram
title <i>org.example.LogicGate$$EnhancerByCGLIB$$580215af</i> 的类图
interface org.example.LogicGate
interface net.sf.cglib.proxy.Factory
interface org.example.LogicGate {
boolean calculate(boolean... input)
}
interface net.sf.cglib.proxy.Factory {
Object newInstance(Callback callback);
Object newInstance(Callback[] callbacks);
Object newInstance(Class[] types, Object[] args, Callback[] callbacks);
Callback getCallback(int index);
void setCallback(int index, Callback callback);
void setCallbacks(Callback[] callbacks);
Callback[] getCallbacks();
}
org.example.LogicGate <|-- org.example.LogicGate$$EnhancerByCGLIB$$580215af
net.sf.cglib.proxy.Factory <|-- org.example.LogicGate$$EnhancerByCGLIB$$580215af
class org.example.LogicGate$$EnhancerByCGLIB$$580215af {
- boolean CGLIB$BOUND
+ static java.lang.Object CGLIB$FACTORY_DATA
- static final java.lang.ThreadLocal CGLIB$THREAD_CALLBACKS
- static final net.sf.cglib.proxy.Callback[] CGLIB$STATIC_CALLBACKS
- net.sf.cglib.proxy.InvocationHandler CGLIB$CALLBACK_0
- static java.lang.Object CGLIB$CALLBACK_FILTER
- static final java.lang.reflect.Method CGLIB$equals$0
- static final java.lang.reflect.Method CGLIB$toString$1
- static final java.lang.reflect.Method CGLIB$hashCode$2
- static final java.lang.reflect.Method CGLIB$clone$3
- static final java.lang.reflect.Method CGLIB$calculate$4
static void CGLIB$STATICHOOK1()
+ final boolean equals(java.lang.Object)
+ final java.lang.String toString()
+ final int hashCode()
# final java.lang.Object clone() throws java.lang.CloneNotSupportedException
+ final boolean calculate(boolean...)
+ org.example.LogicGate$$EnhancerByCGLIB$$580215af()
+ static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[])
+ static void CGLIB$SET_STATIC_CALLBACKS(net.sf.cglib.proxy.Callback[])
- static final void CGLIB$BIND_CALLBACKS(java.lang.Object)
+ java.lang.Object newInstance(net.sf.cglib.proxy.Callback[])
+ java.lang.Object newInstance(net.sf.cglib.proxy.Callback)
+ java.lang.Object newInstance(java.lang.Class[], java.lang.Object[], net.sf.cglib.proxy.Callback[])
+ net.sf.cglib.proxy.Callback getCallback(int)
+ void setCallback(int, net.sf.cglib.proxy.Callback)
+ net.sf.cglib.proxy.Callback[] getCallbacks()
+ void setCallbacks(net.sf.cglib.proxy.Callback[])
}
@enduml
画 "主线逻辑" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startwbs
caption \n\n
' caption 的内容是为了防止掘金平台生成的水印遮盖图中的文字
* 主线逻辑
**:将 <i>this.CGLIB$CALLBACK_0</i> 保存在局部变量 <b><i>var10000</i></b> 中
(两者的类型都是 <i>net.sf.cglib.proxy.InvocationHandler</i>);
** 调用 <b><i>var10000.invoke(Object proxy, Method method, Object[] args)</i><b> 方法
***:<b><i>proxy</i></b> 参数: <i>this</i>
(即当前的代理类对象);
***:<b><i>method</i></b> 参数: <i>CGLIB$calculate$4</i>
(它引用 <i>calculate(boolean...)</i> 方法);
*** <b><i>args</i></b> 参数: <i>new Object[]{var1}</i>
@endwbs