背景
如 [Java] 从 class 文件看 cglib 对 InvocationHandler 的处理 一文所提到的, 使用 cglib 时,会用到 的子接口 ⬇️
它们的简要类图如下 ⬇️
本文关心的是 👇
下图单独展示了 和这两者的关系 👇
要点
Dispatcher 部分
LazyLoader 部分
代码
铺垫
下面举的例子有些牵强,因为我没有想出比较好的例子来 😂 大家凑合看吧。假设有一个 与门 的抽象类 ⬇️
public abstract class AbstractAndGate {
private void validateInputLength(boolean[] input) {
if (input.length == 0) {
throw new IllegalArgumentException("Input should have at least 1 element!");
}
}
public boolean calculate(boolean... input) {
validateInputLength(input);
for (boolean item : input) {
if (!item) {
return false;
}
}
return true;
}
}
AbstractAndGate 是抽象类,实际可用的 与门 是它的两个子类 ⬇️
CheapAndGateExpensiveAndGate
其中 CheapAndGate 的成本很低,即使在每次调用 calculate(boolean) 方法时,都创建新的 CheapAndGate 实例,也没问题。而 ExpensiveAndGate 的成本很高,我们希望最多只持有一个 ExpensiveAndGate 实例。
项目结构
我们在项目顶层执行 tree . 命令,会看到如下的结果 ⬇️
.
├── pom.xml
└── src
├── main
│ └── java
│ └── org
│ └── example
│ ├── AbstractAndGate.java
│ └── GateFactory.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>
AbstractAndGate.java
package org.example;
public abstract class AbstractAndGate {
private void validateInputLength(boolean[] input) {
if (input.length == 0) {
throw new IllegalArgumentException("Input should have at least 1 element!");
}
}
public boolean calculate(boolean... input) {
validateInputLength(input);
for (boolean item : input) {
if (!item) {
return false;
}
}
return true;
}
}
/**
* Assume that CheapAndGate is inexpensive,
* and it is OK to create a new instance for {@link AbstractAndGate#calculate(boolean...)} call.
*/
final class CheapAndGate extends AbstractAndGate {
public CheapAndGate() {
System.out.println("CheapAndGate instance is being created");
}
@Override
public String toString() {
return "CheapAndGate";
}
}
/**
* Assume that ExpensiveAndGate is expensive,
* and we need to cache it for {@link AbstractAndGate#calculate(boolean...)} calls.
*/
final class ExpensiveAndGate extends AbstractAndGate {
public ExpensiveAndGate() {
// Assume that there is some expensive work here
System.out.println("ExpensiveAndGate instance is being created");
}
@Override
public String toString() {
return "ExpensiveAndGate";
}
}
GateFactory.java
package org.example;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Dispatcher;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.LazyLoader;
public class GateFactory {
static {
// 将 cglib 生成的类保存到当前目录下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
}
private static final Dispatcher dispatcher = CheapAndGate::new;
private static final LazyLoader lazyLoader = ExpensiveAndGate::new;
public static AbstractAndGate buildCheapAndGate() {
return (AbstractAndGate) Enhancer.create(AbstractAndGate.class, dispatcher);
}
public static AbstractAndGate buildExpensiveAndGate() {
return (AbstractAndGate) Enhancer.create(AbstractAndGate.class, lazyLoader);
}
}
GateFactoryTest.java
package org.example;
import net.sf.cglib.proxy.Dispatcher;
import net.sf.cglib.proxy.LazyLoader;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
public class GateFactoryTest {
@Test
public void testBuildCheapAndGate() {
AbstractAndGate cheapAndGate = GateFactory.buildCheapAndGate();
Assert.assertFalse(cheapAndGate.calculate(false, false));
Assert.assertFalse(cheapAndGate.calculate(false, true));
Assert.assertTrue(cheapAndGate.calculate(true, true));
}
@Test
public void testBuildExpensiveAndGate() {
AbstractAndGate expensiveAndGate = GateFactory.buildExpensiveAndGate();
Assert.assertFalse(expensiveAndGate.calculate(false, false));
Assert.assertFalse(expensiveAndGate.calculate(false, true));
Assert.assertTrue(expensiveAndGate.calculate(true, true));
}
@Test
public void testDispatcher() throws ReflectiveOperationException {
AbstractAndGate cheapAndGate = GateFactory.buildCheapAndGate();
Field dispatcherField = GateFactory.class.getDeclaredField("dispatcher");
dispatcherField.setAccessible(true);
Dispatcher dispatcher = (Dispatcher) dispatcherField.get(null);
Field cglibCallback0Field = cheapAndGate.getClass().getDeclaredField("CGLIB$CALLBACK_0");
cglibCallback0Field.setAccessible(true);
Dispatcher cglibCallback0 = (Dispatcher) cglibCallback0Field.get(cheapAndGate);
Assert.assertSame(cglibCallback0, dispatcher);
}
@Test
public void testLazyLoader() throws ReflectiveOperationException {
AbstractAndGate expensiveAndGate = GateFactory.buildExpensiveAndGate();
Field lazyLoaderField = GateFactory.class.getDeclaredField("lazyLoader");
lazyLoaderField.setAccessible(true);
LazyLoader lazyLoader = (LazyLoader) lazyLoaderField.get(null);
Field cglibLazyLoader0Field = expensiveAndGate.getClass().getDeclaredField("CGLIB$CALLBACK_0");
cglibLazyLoader0Field.setAccessible(true);
LazyLoader cglibLazyLoader0 = (LazyLoader) cglibLazyLoader0Field.get(expensiveAndGate);
Assert.assertSame(cglibLazyLoader0, lazyLoader);
}
}
AbstractAndGate/CheapAndGate/ExpensiveAndGate/GateFactoryT/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
├── AbstractAndGate$$EnhancerByCGLIB$$4542421f.class
└── AbstractAndGate$$EnhancerByCGLIB$$5ca9308f.class
7 directories, 4 files
以下两个文件看起来和 AbstractAndGate 直接相关
AbstractAndGate$$EnhancerByCGLIB$$4542421f.classAbstractAndGate$$EnhancerByCGLIB$$5ca9308f.class
我们在 Intellij IDEA (Community Edition) 可以看到这两个 class 文件反编译的结果(但完整的结果比较长,这里就不展示了)。下方是它们的类图 ⬇️
分析
我们先看 Dispatcher
Dispatcher
从 IntelliJ IDEA (Community Edition) 反编译的结果(如下图所示)来看,AbstractAndGate$$EnhancerByCGLIB$$5ca9308f 中的下列方法的处理逻辑类似 ⬇️
equals(Object)hashCode()toString()calculate(boolean...)
这些方法在调用 loadObject() 方法后,对它的返回值的处理有些差异(我在下图中用红线把有差异的地方标出来了)。
所以我们着重看一个方法就行了。我们来看看 calculate(boolean...) 方法 ⬇️
public final boolean calculate(boolean... var1) {
Dispatcher var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return ((AbstractAndGate)var10000.loadObject()).calculate(var1);
}
看起来主线逻辑是这样的 ⬇️
那么以下两者是否为同一个引用呢?
AbstractAndGate$$EnhancerByCGLIB$$5ca9308f中的CGLIB$CALLBACK_0字段GateFactory中的dispatcher字段
我用如下的单元测试(上文已提供完整代码)验证了一下 ⬇️ 两者确实为同一个引用
那么,在代理类中使用 Dispatcher 的主线逻辑就可以这样概括了 ⬇️
我们再去看看 LazyLoader
LazyLoader
从 IntelliJ IDEA (Community Edition) 反编译的结果(如下图所示)来看,AbstractAndGate$$EnhancerByCGLIB$$4542421f 中的下列方法的处理逻辑类似 ⬇️
equals(Object)hashCode()toString()calculate(boolean...)
这些方法内部都调用了
CGLIB$LOAD_PRIVATE_0() 方法,后者的逻辑如下 ⬇️
private final synchronized Object CGLIB$LOAD_PRIVATE_0() {
Object var10000 = this.CGLIB$LAZY_LOADER_0;
if (var10000 == null) {
LazyLoader var10001 = this.CGLIB$CALLBACK_0;
if (var10001 == null) {
CGLIB$BIND_CALLBACKS(this);
var10001 = this.CGLIB$CALLBACK_0;
}
var10000 = this.CGLIB$LAZY_LOADER_0 = var10001.loadObject();
}
return var10000;
}
看起来 this.CGLIB$LAZY_LOADER_0 字段的赋值处理是懒式的。假设 this.CGLIB$CALLBACK_0 字段和 中 字段引用的是同一个对象。最初 this.CGLIB$LAZY_LOADER_0 为 null,下图第 43 行的 if 条件成立,第 50 行会给 this.CGLIB$LAZY_LOADER_0 赋值(所赋的值是 的一个实例)
那么之后再执行这个方法时,this.CGLIB$LAZY_LOADER_0 已经不是 null 了,下图第 43 行的 if 语句条件不成立,直接执行第 53 行。
但是假设毕竟是假设,还是要验证一下以下两者是否为同一个引用。
AbstractAndGate$$EnhancerByCGLIB$$4542421f中的CGLIB$CALLBACK_0字段GateFactory中的lazyLoader字段
我用如下的单元测试(上文已提供完整代码)验证了一下 ⬇️ 两者确实为同一个引用
那么,在代理类中使用 LazyLoader 的主线逻辑就可以这样概括了 ⬇️
其他
我把使用 PlantUML 插件绘制本文中若干图片的原始代码保存在 这篇笔记 里了