jdk17分别使用jlink打包javaFX应用和jpackage打包exe(包含第三方库处理)
  5BKASAZUsylB 2023年11月01日 88 0

本文唯一发布网站 博客园(官网地址:https://www.cnblogs.com/)

本文地址:https://www.cnblogs.com/zeromi/p/17227787.html

 

操作环境说明:

操作系统:windows11

(linux也可以参考本文操作)

jdk版本:openjdk-17+35

(理论上jdk9之后都可以按本文操作,具体是否可行,未验证)

javaFX版本:javafx-sdk-17.0.2

控制台:powershell

本文前提:项目代码需要已编译通过,笔者已经使用idea2022社区版编译通过,当然你也可以选择手动编译,使用IDE可以帮助我们更快完成这件事

powershell命令行太长时可以换行输入,windows下使用"`"换行,linux下使用"\"换行

 

1.对第三方jar进行模块信息注入

本项目导入的第三方库除javafx外,还有abmq-client-xxx.jar,而abmq-client-xxx.jar又引用了slf4j-api-xx.jar,javafx本身已经时模块化的,所以无需处理所以需要对剩下的这两个jar进行处理

建议按自底向上的方式进行处理,先处理slf4j-api-xx.jar,至于原因,下面会说明,这里我为了演示,先对abmq-client-xxx.jar注入模块信息

 

1.1.生成模块信息

jdeps是jdk自带的依赖分析工具

jdeps参数说明:

--ignore-missing-deps:忽略错误依赖,否则会解析出来很多错误
--generate-module-info: 生成模块信息文件module-info.java,后面是模块信c息文件输出位置
jdeps --ignore-missing-deps --generate-module-info . amqp-client-5.16.0.jar

执行完后,会发现当前文件夹出现了一个名为 com.rabbitmq.client的文件夹,里面就是生成的模块文件module-info.java

打印信息如下:

PS E:\codes\myidea\fxdemo> jdeps --ignore-missing-deps --generate-module-info . amqp-client-5.16.0.jar
Warning: --ignore-missing-deps specified. Missing dependencies from com.rabbitmq.client are ignored
writing to .\com.rabbitmq.client\module-info.java
PS E:\codes\myidea\fxdemo>

 

1.2.编译module-info.java

javac是jdk自带的编译器

需要将生成的module-info.java编译成class文件

javac参数说明:

-p 模块引用的第三方库,--module-path的参数简写
--patch-module 当前要打补丁的模块名和jar文件,参数形式:模块名=jar路径,示例:com.rabbitmq.client=amqp-client-5.16.0.jar或com.rabbitmq.client=./amqp-client-5.16.0.jar
javac -p .\slf4j-api-1.7.36.jar --patch-module com.rabbitmq.client=amqp-client-5.16.0.jar com.rabbitmq.client/module-info.java

结果发现编译报错,报错信息如下: 

PS E:\codes\myidea\fxdemo> javac --patch-module com.rabbitmq.client=amqp-client-5.16.0.jar com.rabbitmq.client/module-info.java
com.rabbitmq.client\module-info.java:12: 错误: 程序包为空或不存在: com.rabbitmq.tools
    exports com.rabbitmq.tools;
                        ^
1 个错误
PS E:\codes\myidea\fxdemo>

我苦思冥想,为啥“exports com.rabbitmq.client”没有报错,而“exports com.rabbitmq.tools”报错了,对比了一下差异,在amqp-client-5.16.0.jar文件的目录发现原因。

原来com.rabbitmq.tools文件夹下面没有class文件只有子文件夹,而com.rabbitmq.client文件夹下面有class文件,导致这个包无法导出。
知道原因,那么解决起来就简单了,用记事本打开module-info.java,删除exports com.rabbitmq.tools这行就行了。

重新编译顺利通过。

 

1.3.注入模块信息到jar文件

jar是jdk自带的一个打包工具

jar参数说明:

u:--update,更新现有 jar 档案
f:--file=FILE,档案文件名。省略时, 基于操作使用 stdin 或 stdout(就是会根据命令内容使用输入流或输出流)
-C:DIR,更改为指定的目录并包含以下文件

注意:这一步会修改这个jar文件,建议操作前备份jar文件

jar uf amqp-client-5.16.0.jar -C com.rabbitmq.client module-info.class

执行完后,使用压缩软件可以查看到jar文件内部根目录下已经多了一个module-info.class,到这里我们的对amqp-client-5.16.0.jar的模块信息注入就完成了。

接着slf4j-api-xxx.jar也按照上述流程执行一遍,完成模块信息注入。

 

1.4.运行java程序

java是java程序运行的基础命令

java参数说明:

 -p:等价于--module-path,所有第三方类文件或jar文件目录,win使用“;”间隔,linux使用“:”间隔
  "E:\codes\myidea\fxdemo\lib"
    文件夹中是引用的第三方库slf4j-api-xxx.jar,ampq-client-xxx.jar,这两个模块会自动转换成自动模块所以不会报错
    已经通过手动注入module-info,所以不会报模块错误
     这个看项目情况
  "E:\codes\myidea\fxdemo\target\classes"
    当前项目编译好的class文件的包根目录。 这个必须有,否则会报错找不到当前项目模块
  "D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib"
    包含javafx的jar文件,javafx从jdk9开始从jdk中剥离出来了,也属于第三方,因为本项目使用了javaFX,所以这个 看项目情况
--add-modules:所有引用的第三方包的模块名,使用“,”分隔

执行下列命令:

java -p "E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes;D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib" `
--add-modules=org.slf4j,com.rabbitmq.client,javafx.controls,javafx.fxml `
-m com.example.fxdemo/com.example.fxdemo.HelloApplication

很快啊,一个报错信息就拍我脸上了,内容如下:

PS E:\codes\myidea\fxdemo> java -p "E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes;D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib" `
>> --add-modules=org.slf4j,com.rabbitmq.client,javafx.controls,javafx.fxml `
>> -m com.example.fxdemo/com.example.fxdemo.HelloApplication
Exception in Application start method
java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalAccessError: class com.rabbitmq.client.ConnectionFactory (in module com.rabbitmq.client) cannot access class org.slf4j.LoggerFactory (in module org.slf4j) because module com.rabbitmq.client does not read module org.slf4j
        at com.rabbitmq.client/com.rabbitmq.client.ConnectionFactory.<clinit>(ConnectionFactory.java:55)
        at com.example.fxdemo/com.example.fxdemo.MsgSendController.<clinit>(MsgSendController.java:48)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
        at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:939)
        at javafx.fxml/javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:981)
        at javafx.fxml/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
        at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:755)
        at javafx.fxml/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2808)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2634)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
        at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2516)
        at com.example.fxdemo/com.example.fxdemo.HelloApplication.start(HelloApplication.java:33)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
        ... 1 more
Exception running application com.example.fxdemo.HelloApplication
PS E:\codes\myidea\fxdemo>

我一看

cannot access class org.slf4j.LoggerFactory

does not read module org.slf4j

这意思不就是找不到模块org.slf4j吗,我用记事本打开之前的生成的module-info.java一看,果然模块根本就没有引用org.slf4j

赶紧在里面加上一行(对于未模块化的jar,模块名一般就取包的根目录名,包根目录指从顶层开始一直到有class文件的目录)

这也是我为啥建议按照依赖自底向上进行模块化注入,因为把依赖理清之后,才知道模块信息哪些地方需要修改

requires org.slf4j;

最终的module-info.java内容如下:

module com.rabbitmq.client {
    requires java.naming;
    requires java.security.sasl;
    requires java.sql;
    requires org.slf4j;

    requires transitive java.desktop;

    exports com.rabbitmq.client;
    exports com.rabbitmq.client.impl;
    exports com.rabbitmq.client.impl.nio;
    exports com.rabbitmq.client.impl.recovery;
    exports com.rabbitmq.tools.json;
    exports com.rabbitmq.tools.jsonrpc;
    exports com.rabbitmq.utility;

}

重新执行上述命令,对模块信息编译,这下应该稳了吧,然而:

PS E:\codes\myidea\fxdemo\lib> javac --patch-module com.rabbitmq.client=./amqp-client-5.16.0.jar com.rabbitmq.client/module-info.java
module-info.java:5: 错误: 找不到模块: org.slf4j requires org.slf4j; ^ 1 个错误 PS E:\codes\myidea\fxdemo\lib>

这个是因为amqp-client-5.16.0.jar引用了slf4j-api-xx.jar,所以我们只要在编译的时候带上slf4j-api-xx.jar的路径就行了,使用-p参数即可

个人猜想:

  这里slf4j-api-1.7.36.jar还没有模块化,但是仍然可以编译通过,猜测可能是slf4j-api-xxx.jar被转成了自动模块(未验证)

  这里如果两个jar都没有模块化是可以直接使用上述运行命令启动程序的(已验证)

javac -p ./slf4j-api-1.7.36.jar --patch-module com.rabbitmq.client=./amqp-client-5.16.0.jar ./com.rabbitmq.client/module-info.java

编译顺利通过,重复上述模块注入过程,分别对amqp-client-5.16.0.jar和slf4j-api-1.7.36.jar模块化后,再次运行java程序,ok启动成功,随意操作两下,没有出现任何问题,下一步开始打包。

 

2.jlink打包

jlink打包项目com.example.fxdemo,项目主类com.example.fxdemo/com.example.fxdemo.HelloApplication
引用了第三方模块com.rabbitmq.client,org.slf4j
jar名称分别为abmq-client-xxx.jar,slf4j-api-xx.jar
这两个jar已经被手动注入了模块信息
 
jlink参数说明:
--module-path: 引用的第三方包目录(目录中包含jar或jmod文件),存在多个参数时,windows下使用";"分隔,linux下使用":"分隔
--add-modules:添加当前项目的要打包的模块名,存在多个参数时,使用","分隔
--output:打包文件输出目录
--launcher:启动选项,启动命令=模块名/启动类(全限定名,即带包名的)
--strip-debug 不打包调式信息
--compress=2 开启2级别压缩
 

2.1.jlink普通打包(大小约90M)

jlink打包运行环境(jlink不支持自动模块,所以引用链条上必须都是模块化的,否则需要手动转成模块化包)
 
使用javaFX的jar库打包(方式一)
好处是无需再下载javaFX的jmod包
jlink --module-path "D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib;E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes" `
--add-modules com.example.fxdemo `
--output app
 
使用javaFX的jmod库打包(方式二)
这里笔者系统为64位的win平台,所以下载的文件为openjfx-17.0.2_windows-x64_bin-jmods.zip,可以通过javaFX官网找到下载链接,找到后解压即可
jlink --module-path "D:\ProgramFiles\Java\javafx\javafx-jmods-17.0.2;E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes" `
--add-modules com.example.fxdemo `
--output app
运行应用,参数为主类,不需要带模块名
cd app
./bin/java com.example.fxdemo.HelloApplication
 

2.2.jlink开启最高压缩选项打包(zip包,大小约45M,缩小了一半)

这个使用了--launcher选项,添加该选项后,jlink会根据你设定的名称在输出目录的bin文件夹下创建两个文件

本例使用的参数设置为Hello,那么生成的文件是Hello和Hello.bat

使用javaFX的jar库打包(方式一)
jlink --module-path "D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib;E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes" `
--add-modules com.example.fxdemo --output appCompress `
--launcher Hello=com.example.fxdemo/com.example.fxdemo.HelloApplication --strip-debug --compress=2
 
使用javaFX的jmod库打包(方式二)
这里笔者系统为64位的win平台,所以下载的文件为openjfx-17.0.2_windows-x64_bin-jmods.zip,可以通过javaFX官网找到下载链接,找到后解压即可
jlink --module-path "D:\ProgramFiles\Java\javafx\javafx-jmods-17.0.2;E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes" `
--add-modules com.example.fxdemo --output appCompress `
--launcher Hello=com.example.fxdemo/com.example.fxdemo.HelloApplication --strip-debug --compress=2

运行应用

运行应用(下面这个命令经测试是执行的Hello.bat,Hello文件无法执行)
./bin/Hello

或者

./bin/java com.example.fxdemo.HelloApplication

 

4.jpackage打包成exe程序

要求:

  必需:安装wix3.0版本(jdk17还不支持wix4.0),

  非必需:对应平台的jmods,如果项目中使用到了javafx,就需要下载这个,否则打包会报错,具体错误可以查看本文目录错误记录:exe运行不成功

      这里笔者系统为64位的win平台,所以下载的文件为openjfx-17.0.2_windows-x64_bin-jmods.zip,可以通过javaFX官网找到下载链接,找到后解压即可

打包成exe后,大小在为26.6M,生成的exe会带一个安装引导程序

具体请参考笔者另一篇文章:

使用jpackage将java程序打包成exe程序(不需要安装jdk即可运行)(https://www.cnblogs.com/zeromi/p/14852323.html

jpackage --type exe -p "E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes;D:\ProgramFiles\Java\javafx\javafx-jmods-17.0.2" `
-n fxdemo -m com.example.fxdemo/com.example.fxdemo.HelloApplication `
--add-modules java.base,org.slf4j,com.rabbitmq.client,javafx.controls,javafx.fxml `
--vendor cy --verbose --win-console --win-dir-chooser --win-shortcut

 实际上不加--add-modules也可以打包

jpackage --type exe -p "E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes;D:\ProgramFiles\Java\javafx\javafx-jmods-17.0.2" `
-n fxdemo -m com.example.fxdemo/com.example.fxdemo.HelloApplication `
--vendor cy --verbose --win-console --win-dir-chooser --win-shortcut

 

 

5.错误记录

找不到模块错误

PS E:\codes\myidea\fxdemo> java -p "E:\codes\myidea\fxdemo\lib;E:\codes\myidea\fxdemo\target\classes;D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib" ` >> --add-modules=org.slf4j,com.rabbitmq.client,javafx.controls,javafx.fxml ` >> -m com.example.fxdemo/com.example.fxdemo.HelloApplication Error occurred during initialization of boot layer java.lang.module.FindException: Error reading module: E:\codes\myidea\fxdemo\lib\com.rabbitmq.client Caused by: java.lang.module.InvalidModuleDescriptorException: Package com.rabbitmq.client.impl.nio not found in module PS E:\codes\myidea\fxdemo>

原因是“E:\codes\myidea\fxdemo\out\artifacts\fxdemo\lib”目录中存在文件夹“com.rabbitmq.client”,导致java认为这个是正常的包需要解析,但是这个文件夹中只有module-info.java和module-info.class

注意清理掉编译文件夹中无关的文件,否则可能会引起异常报错,或编译的程序出现异常行为

 

安装打包好的exe后,双击图标黑窗口一闪而过

找到我们的程序安装目录,笔者安装位置:D:\ProgramFiles\fxdemo

然后鼠标右键打开powershell,执行下列命令手动启动

.\fxdemo.exe

报错信息如下:

PS D:\ProgramFiles\fxdemo> .\fxdemo.exe
Graphics Device initialization failed for :  d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(Unknown Source)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.init(Unknown Source)
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(Unknown Source)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(Unknown Source)
        at java.base/java.lang.Thread.run(Unknown Source)
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.base/java.lang.reflect.Method.invoke(Unknown Source)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: No toolkit found
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
        ... 5 more
Failed to launch JVM
PS D:\ProgramFiles\fxdemo>

看到报错是no suitable pipeline found,No toolkit found,猜测是因为没有引用jmod打包(这里用的jar打包),换jmod文件打包就好了,打包命令:打包命令

 

 

附:

模块分析

可以直接使用idea等IDE查看依赖关系,尤其是在使用maven的情况下,建议使用IDE。

jdeps经测试仅支持jar,class文件依赖分析,不支持jmod

分析模块com.example.fxdemo依赖:

--module-path:依赖库路径

-m:指定解析根模块

jdeps --module-path "E:\codes\myidea\fxdemo\lib;D:\ProgramFiles\Java\javafx\javafx-sdk-17.0.2\lib;E:\codes\myidea\fxdemo\target\classes" -m com.example.fxdemo

 分析模块com.rabbitmq.client依赖:

jdeps --module-path "E:\codes\myidea\fxdemo\lib" -m com.rabbitmq.client

 

参考和引用

参考和引用下列文章中的部分内容,未经过下列作者同意,如有存疑和其他问题请联系本人删除

Project Jigsaw: Quick Start Guide (openjdk.org)( https://openjdk.org/projects/jigsaw/quick-start

java9 揭秘 jlink_使用jlink打包的java应用(https://blog.csdn.net/weixin_29781865/article/details/114759641

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  2Vtxr3XfwhHq   2024年05月17日   55   0   0 Java
  Tnh5bgG19sRf   2024年05月20日   113   0   0 Java
  8s1LUHPryisj   2024年05月17日   48   0   0 Java
  aRSRdgycpgWt   2024年05月17日   47   0   0 Java
5BKASAZUsylB