背景
如 [Java] 从 class 文件看 cglib 对 InvocationHandler 的处理 一文所提到的, 使用 cglib 时,会用到 的子接口 ⬇️
它们的简要类图如下 ⬇️
在使用 时,cglib 所生成的动态代理类是怎样的呢?让我们一起探索吧。
注意:本文所提到的 都是指
要点
假设类 中有 方法。我们可以通过如下代码,为 生成动态代理类 。
(C) Enhancer.create(C.class, NoOp.INSTANCE);
但是 中并没有 override 里的方法。所以对 的实例: 而言,调用 方法时,执行的其实是 中的 方法。
代码
铺垫
假设有一个 与门 的类 ⬇️
public class AndGate {
private void validateLength(boolean[] input) {
if (input.length == 0) {
throw new IllegalArgumentException("And gate should have at least one input!");
}
}
public boolean calculate(boolean... input) {
validateLength(input);
for (boolean item : input) {
if (!item) {
return false;
}
}
return true;
}
}
我们希望用 cglib 为其生成动态代理,但是这个代理类不需要实现任何额外的逻辑(因为 AndGate 的功能已经够用了),那么我们可以用 。(这个场景比较牵强,按照我的理解 应该是和 的其他子接口一起使用的,单独使用它似乎没什么意义)
项目结构
我们在项目顶层执行 tree . 命令,会看到如下的结果 ⬇️
.
├── pom.xml
└── src
├── main
│ └── java
│ └── org
│ └── example
│ ├── AndGate.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>
AndGate.java
package org.example;
public class AndGate {
private void validateLength(boolean[] input) {
if (input.length == 0) {
throw new IllegalArgumentException("And gate should have at least one input!");
}
}
public boolean calculate(boolean... input) {
validateLength(input);
for (boolean item : input) {
if (!item) {
return false;
}
}
return true;
}
}
GateFactory.java
package org.example;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
public class GateFactory {
static {
// 将 cglib 生成的类保存到当前目录下
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
}
public static AndGate buildAndGate() {
return (AndGate) Enhancer.create(AndGate.class, NoOp.INSTANCE);
}
}
GateFactoryTest.java
package org.example;
import org.junit.Assert;
import org.junit.Test;
public class GateFactoryTest {
@Test
public void testBuildAndGate() {
AndGate andGate = GateFactory.buildAndGate();
Assert.assertTrue(andGate.calculate(true, true));
Assert.assertFalse(andGate.calculate(true, false));
Assert.assertFalse(andGate.calculate(false, false));
Assert.assertFalse(andGate.calculate(false, false));
}
}
AndGate/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
└── AndGate$$EnhancerByCGLIB$$4b09a444.class
7 directories, 3 files
AndGate$$EnhancerByCGLIB$$4b09a444.class 看起来和 AndGate 直接相关。我们在 Intellij IDEA (Community Edition) 可以看到前者反编译的结果(完整的结果有点长,这里就不展示了)。下方是 AndGate$$EnhancerByCGLIB$$4b09a444 的类图
分析
从上一小节的类图可以看出,AndGate$$EnhancerByCGLIB$$4b09a444 中并没有 override calculate(boolean...) 方法。那么当我们用 AndGate$$EnhancerByCGLIB$$4b09a444 的实例调用 calculate(boolean...) 方法时,实际上会执行它的父类(即 )中的 calculate(boolean...) 方法。从它的 javadoc 也可以印证这一点 ⬇️
其他
画 "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
note top of net.sf.cglib.proxy.NoOp
它是本文的主角
end note
@enduml
画 "三个类的的类图" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
'https://plantuml.com/class-diagram
title 三个类的的类图
class org.example.AndGate {
- void validateLength(boolean[])
+ boolean calculate(boolean...)
}
class org.example.GateFactory {
+ {static} AndGate buildAndGate()
}
class org.example.GateFactoryTest {
+ void testBuildAndGate()
}
@enduml
画 "org.example.AndGate$$EnhancerByCGLIB$$4b09a444 的类图" 用到的代码
我用了 PlantUML 的插件来画那张图,所用到的代码如下 ⬇️
@startuml
'https://plantuml.com/class-diagram
title <i>org.example.AndGate$$EnhancerByCGLIB$$4b09a444</i> 的类图
interface net.sf.cglib.proxy.Factory
class org.example.AndGate {
- void validateLength(boolean[] input)
+ 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()
}
class org.example.AndGate$$EnhancerByCGLIB$$4b09a444
net.sf.cglib.proxy.Factory <|.. org.example.AndGate$$EnhancerByCGLIB$$4b09a444
org.example.AndGate <|-- org.example.AndGate$$EnhancerByCGLIB$$4b09a444
class org.example.AndGate$$EnhancerByCGLIB$$4b09a444 {
- boolean CGLIB$BOUND
+ {static} Object CGLIB$FACTORY_DATA
- {static} final ThreadLocal CGLIB$THREAD_CALLBACKS
- {static} final Callback[] CGLIB$STATIC_CALLBACKS
- NoOp CGLIB$CALLBACK_0
- {static} Object CGLIB$CALLBACK_FILTER
{static} void CGLIB$STATICHOOK1()
+ AndGate$$EnhancerByCGLIB$$4b09a444()
+ {static} void CGLIB$SET_THREAD_CALLBACKS(Callback[])
+ {static} void CGLIB$SET_STATIC_CALLBACKS(Callback[])
- {static} final void CGLIB$BIND_CALLBACKS(Object)
+ Object newInstance(Callback[])
+ Object newInstance(Callback)
+ Object newInstance(Class[], Object[], Callback[])
+ net.sf.cglib.proxy.Callback getCallback(int)
+ void setCallback(int, Callback)
+ Callback[] getCallbacks()
+ void setCallbacks(Callback[])
}
@enduml