在写插件的时候不可避免的需要调用一些外部库,我们需要把依赖打包到jar中,当使用的依赖过多时体积将非常臃肿。通过 Loading Dependencies from External Sources in Spigot这篇文章和阅读luckperms的解决方法,花了半个通宵捣鼓出了一种可行的方案

最初的解决方案是通过ClassLoader的addURL动态的加载依赖,但就像上面那篇文章说的,java16以上的反射不允许这样的操作;最后我找到了这个仓库 https://github.com/Revxrsal/PluginLib/tree/master/src/main/java/pluginlib

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
Field ucpField = URLClassLoader.class.getDeclaredField("ucp");
ucp = unsafe.getObject(AifadianPay.class.getClassLoader(), unsafe.objectFieldOffset(ucpField));
Field lookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
MethodHandles.Lookup lookup = (MethodHandles.Lookup) unsafe.getObject(unsafe.staticFieldBase(lookupField), unsafe.staticFieldOffset(lookupField));
addUrlHandle = lookup.findVirtual(ucp.getClass(), "addURL", MethodType.methodType(void.class, URL.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

它使用Unsafe绕过安全检查,像魔法一样的取得了 addURL 方法,经过测试,在jdk8到17的版本都运行良好。
这是一个最后更新于2020年的库,我去除了一些无用的功能(对我来说),整理到了https://github.com/meteorOSS/SpigotDependencyManager

接下来,示范一下使用这个新工具来写代码
首先下载上方git仓库发布的jar包,新建一个maven项目,使用
mvn install:install-file -Dfile=src/lib/DependencyManager-1.0-SNAPSHOT.jar -DgroupId=com. meteor -DartifactId=dependency-manager -Dversion=1.0 -Dpackaging=jar
来导入到本地仓库,然后定义依赖

1
2
3
4
5
<dependency>
<groupId>com.meteor</groupId>
<artifactId>dependency-manager</artifactId>
<version>1.0</version>
</dependency>

接下来,想想往常要干什么,比方说我有一个要发起网络请求的功能,想使用Httpclient库来操作。那么,引入依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.core5/httpcore5-h2 -->
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5-h2</artifactId>
<version>5.2.4</version>
</dependency>

接下来就是重头戏了,在plugin.yml中定义依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
name: TestPlugin
main: com.test.plugin.TestPluginMain
version: 1.0
author: meteor
api-version: 1.13
# 所需依赖
lib:
httpcore5:
url: 'https://repo.maven.apache.org/maven2/'
groupId: 'org.apache.httpcomponents.core5'
artifactId: 'httpcore5'
version: '5.2.4'
httpclient:
url: 'https://repo.maven.apache.org/maven2/'
groupId: 'org.apache.httpcomponents.client5'
artifactId: 'httpclient5'
version: '5.2.3'

在主类中,只需要这样

1
2
3
4
5
   @Override
public void onEnable() {
// 载入依赖
DependencyManager.loadDependency(this);
}

最后别忘了把DependcyManager打包到你的jar中,这是唯一需要内置的依赖。我使用maven-assembly-plugin
mvn clean package,在target中找到 xxx-jar-with-dependencies.jar,这就是打包的结果了!
image.png
运行,现在启动的时候如未存在依赖,会通过maven仓库下载到插件目录下的lib里并动态添加!
不过这种方式有没有其他隐患还待验证