ProGuard 破坏 JavaFX 应用程序
ProGuard breaks JavaFX application
我试图混淆我的 JavaFX 应用程序,但它失败了。生成的结果不起作用,我不明白为什么。生成的 jar 失败,因为 fxml 文件无法再加载所有导入 (ClassNotFoundException)。
部署工作流程:
- 构建可运行的 jar(在 IntelliJ 中被称为工件)
- 用 ProGuard 混淆那个 jar
- 修复该 jar 中 ProGuard 无法执行的一些问题
1) 最小示例应用程序
示例应用程序 'GuardTest' 是一个由 3 个 classes 组成的 IntelliJ 项目。
- sample.Main:包含应用程序入口点并加载 GUI fxml 文件 'sample.fxml'
- sample.Controller:'sample.fxml'
的控制器 class
- controls.CustomControl:一个简单的javafx控件,继承自HBox。这用于 'sample.fxml'
'sample.fxml'的内容:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import controls.CustomControl?>
<VBox fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<children>
<CustomControl>
<children>
<Button text="Test"></Button>
</children>
</CustomControl>
</children>
</VBox>
2) 混淆
现在,我将 ProGuard 用于从上述项目生成的结果 jar 文件。我使用以下设置:
-target 8
-injars ./out/artifacts/JavaFXApp/JavaFXApp.jar
-outjars ./out/obfuscated/Obfuscated.jar
-ignorewarnings
-printmapping ./out/obfuscated/proguard.map
-dontusemixedcaseclassnames
-dontshrink
-dontoptimize
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#-flattenpackagehierarchy
-repackageclasses 'p'
-allowaccessmodification
-libraryjars "<java.home>/lib/rt.jar"
-libraryjars "<java.home>/lib/javaws.jar"
-libraryjars "<java.home>/lib/ext/jfxrt.jar"
-adaptresourcefilecontents **.fxml,**.properties,META-INF/MANIFEST.MF,images/*.jar,publicCerts.store,production.version
-keepattributes javafx.fxml.FXML,Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
-keepclassmembers class * {
@javafx.fxml.FXML *;
}
-keepclassmembernames public class com.javafx.main.Main, com.nywelt.sharkload.application.Main {
public static void main(java.lang.String[]);
}
-keepclasseswithmembers public class com.javafx.main.Main, com.product.main.EntryFX, net.license.LicenseEntryPoint {
public *; public static *;
}
3) 修复一些(明显的)ProGuard 故障
生成的 jar 文件 'Obfuscated.jar' 具有以下结构:
**Obfuscated.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
- sample
--> sample.fxml
主要 class 通过使用以下行加载 'sample.fxml' 文件来启动 GUI:
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
因此,我必须将 'sample.fxml' 文件也移动到文件夹 p 中,以使上面的行再次运行。我还修复了 fxml 文件中 ProGuard 忘记更改(现已混淆)class 名称的一些问题。
现在结构如下所示:
**Obfuscated_fixed.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
--> sample.fxml
sample.fxml 文件现在看起来像这样:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import p.a?>
<VBox fx:controller="p.b"
xmlns:fx="http://javafx.com/fxml">
<children>
<a>
<children>
<Button text="Test"></Button>
</children>
</a>
</children>
</VBox>
问题
现在这个 jar 应该真的可以再次使用了,因为一切都恢复正常了。但事实并非如此! fxml 加载程序无法加载 CustomControl(现在 named/obfuscated 'a.class')。这是为什么?
启动 jar 文件时出现以下错误输出(我是 运行 java 版本 1.8.0_40):
E:\Eigene Programme\GuardTest\out\obfuscated>java -jar Obfuscated_fixed.jar
Exception in Application start method
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown So
urce)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication2(
Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/849460928.run(Unkn
own Source)
at java.lang.Thread.run(Unknown Source)
Caused by: javafx.fxml.LoadException:
file:/E:/Eigene%20Programme/GuardTest/out/obfuscated/Obfuscated_fixed.jar!/p/sam
ple.fxml
at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
at javafx.fxml.FXMLLoader.importClass(Unknown Source)
at javafx.fxml.FXMLLoader.processImport(Unknown Source)
at javafx.fxml.FXMLLoader.processProcessingInstruction(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at p.c.start(Main.java:13)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication19
(Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/663980963.run(Unkn
own Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait2(Unknown
Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/410424423.run(Unkn
own Source)
at com.sun.javafx.application.PlatformImpl.lambda$null0(Unknown Sourc
e)
at com.sun.javafx.application.PlatformImpl$$Lambda/1149216748.run(Unk
nown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater1(Unknown S
ource)
at com.sun.javafx.application.PlatformImpl$$Lambda/1963387170.run(Unk
nown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/237061348.run(Unknown
Source)
... 1 more
Caused by: java.lang.ClassNotFoundException
at javafx.fxml.FXMLLoader.loadType(Unknown Source)
... 26 more
E:\Eigene Programme\GuardTest\out\obfuscated>Pause
Drücken Sie eine beliebige Taste . . .
在主 class 和
中设置默认 class 加载程序
FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader());
也无济于事。
项目文件
在这里你可以找到示例项目(IntelliJ):
https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0
IntelliJ生成的jar编译为:
./out/artifacts/JavaFXApp/JavaFXApp.jar
混淆后的 Jar 位于:
./out/obfuscated/Obfuscated.jar
经过混淆但已修复(至少应该是)的 jar,如上所述:
./out/obfuscated/Obfuscated_fixed.jar
为了表明 'sample.fxml' 文件中的导入语句导致了问题,我从 fxml 文件中删除了我的自定义控件并将其保存到(工作)jar 中:
./out/obfuscated/Obfuscated_fixed_work.jar
很抱歉问了这么长的问题。无论如何我希望你能帮助我:)
我找到解决办法了!问题是 FXML 无法导入不以大写字母开头的 classes。因此,必须提供自己的可用名称列表,ProGuard 会使用该列表进行混淆。这是通过以下方式完成的:
-classobfuscationdictionary obfuscationClassNames.txt
obfuscationClassNames.txt 包含可用 class 名称的行分隔列表:
A
B
C
D
...
我使用了相同的步骤。我可以看到导入已更新到 fxml 文件中,但它们的使用不是。
获取异常:
javafx.fxml.LoadException: MenuBarControl is not a valid type.
但是如果我看到 fxml 文件导入已更新但未使用。
< ? import q.A ? >
< ? import r.A ? >
<VBox fx:id="top">
<MenuBarControl fx:id="menuBarControl"/>
</VBox>
我试图混淆我的 JavaFX 应用程序,但它失败了。生成的结果不起作用,我不明白为什么。生成的 jar 失败,因为 fxml 文件无法再加载所有导入 (ClassNotFoundException)。
部署工作流程:
- 构建可运行的 jar(在 IntelliJ 中被称为工件)
- 用 ProGuard 混淆那个 jar
- 修复该 jar 中 ProGuard 无法执行的一些问题
1) 最小示例应用程序
示例应用程序 'GuardTest' 是一个由 3 个 classes 组成的 IntelliJ 项目。
- sample.Main:包含应用程序入口点并加载 GUI fxml 文件 'sample.fxml'
- sample.Controller:'sample.fxml' 的控制器 class
- controls.CustomControl:一个简单的javafx控件,继承自HBox。这用于 'sample.fxml'
'sample.fxml'的内容:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import controls.CustomControl?>
<VBox fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<children>
<CustomControl>
<children>
<Button text="Test"></Button>
</children>
</CustomControl>
</children>
</VBox>
2) 混淆
现在,我将 ProGuard 用于从上述项目生成的结果 jar 文件。我使用以下设置:
-target 8
-injars ./out/artifacts/JavaFXApp/JavaFXApp.jar
-outjars ./out/obfuscated/Obfuscated.jar
-ignorewarnings
-printmapping ./out/obfuscated/proguard.map
-dontusemixedcaseclassnames
-dontshrink
-dontoptimize
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
#-flattenpackagehierarchy
-repackageclasses 'p'
-allowaccessmodification
-libraryjars "<java.home>/lib/rt.jar"
-libraryjars "<java.home>/lib/javaws.jar"
-libraryjars "<java.home>/lib/ext/jfxrt.jar"
-adaptresourcefilecontents **.fxml,**.properties,META-INF/MANIFEST.MF,images/*.jar,publicCerts.store,production.version
-keepattributes javafx.fxml.FXML,Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
-keepclassmembers class * {
@javafx.fxml.FXML *;
}
-keepclassmembernames public class com.javafx.main.Main, com.nywelt.sharkload.application.Main {
public static void main(java.lang.String[]);
}
-keepclasseswithmembers public class com.javafx.main.Main, com.product.main.EntryFX, net.license.LicenseEntryPoint {
public *; public static *;
}
3) 修复一些(明显的)ProGuard 故障
生成的 jar 文件 'Obfuscated.jar' 具有以下结构:
**Obfuscated.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
- sample
--> sample.fxml
主要 class 通过使用以下行加载 'sample.fxml' 文件来启动 GUI:
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
因此,我必须将 'sample.fxml' 文件也移动到文件夹 p 中,以使上面的行再次运行。我还修复了 fxml 文件中 ProGuard 忘记更改(现已混淆)class 名称的一些问题。
现在结构如下所示:
**Obfuscated_fixed.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
--> sample.fxml
sample.fxml 文件现在看起来像这样:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import p.a?>
<VBox fx:controller="p.b"
xmlns:fx="http://javafx.com/fxml">
<children>
<a>
<children>
<Button text="Test"></Button>
</children>
</a>
</children>
</VBox>
问题
现在这个 jar 应该真的可以再次使用了,因为一切都恢复正常了。但事实并非如此! fxml 加载程序无法加载 CustomControl(现在 named/obfuscated 'a.class')。这是为什么?
启动 jar 文件时出现以下错误输出(我是 运行 java 版本 1.8.0_40):
E:\Eigene Programme\GuardTest\out\obfuscated>java -jar Obfuscated_fixed.jar
Exception in Application start method
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown So
urce)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication2(
Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/849460928.run(Unkn
own Source)
at java.lang.Thread.run(Unknown Source)
Caused by: javafx.fxml.LoadException:
file:/E:/Eigene%20Programme/GuardTest/out/obfuscated/Obfuscated_fixed.jar!/p/sam
ple.fxml
at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
at javafx.fxml.FXMLLoader.importClass(Unknown Source)
at javafx.fxml.FXMLLoader.processImport(Unknown Source)
at javafx.fxml.FXMLLoader.processProcessingInstruction(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
at javafx.fxml.FXMLLoader.load(Unknown Source)
at p.c.start(Main.java:13)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication19
(Unknown Source)
at com.sun.javafx.application.LauncherImpl$$Lambda/663980963.run(Unkn
own Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait2(Unknown
Source)
at com.sun.javafx.application.PlatformImpl$$Lambda/410424423.run(Unkn
own Source)
at com.sun.javafx.application.PlatformImpl.lambda$null0(Unknown Sourc
e)
at com.sun.javafx.application.PlatformImpl$$Lambda/1149216748.run(Unk
nown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater1(Unknown S
ource)
at com.sun.javafx.application.PlatformImpl$$Lambda/1963387170.run(Unk
nown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null5(Unknown Source)
at com.sun.glass.ui.win.WinApplication$$Lambda/237061348.run(Unknown
Source)
... 1 more
Caused by: java.lang.ClassNotFoundException
at javafx.fxml.FXMLLoader.loadType(Unknown Source)
... 26 more
E:\Eigene Programme\GuardTest\out\obfuscated>Pause
Drücken Sie eine beliebige Taste . . .
在主 class 和
中设置默认 class 加载程序FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader());
也无济于事。
项目文件
在这里你可以找到示例项目(IntelliJ): https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0
IntelliJ生成的jar编译为: ./out/artifacts/JavaFXApp/JavaFXApp.jar
混淆后的 Jar 位于: ./out/obfuscated/Obfuscated.jar
经过混淆但已修复(至少应该是)的 jar,如上所述: ./out/obfuscated/Obfuscated_fixed.jar
为了表明 'sample.fxml' 文件中的导入语句导致了问题,我从 fxml 文件中删除了我的自定义控件并将其保存到(工作)jar 中: ./out/obfuscated/Obfuscated_fixed_work.jar
很抱歉问了这么长的问题。无论如何我希望你能帮助我:)
我找到解决办法了!问题是 FXML 无法导入不以大写字母开头的 classes。因此,必须提供自己的可用名称列表,ProGuard 会使用该列表进行混淆。这是通过以下方式完成的:
-classobfuscationdictionary obfuscationClassNames.txt
obfuscationClassNames.txt 包含可用 class 名称的行分隔列表:
A
B
C
D
...
我使用了相同的步骤。我可以看到导入已更新到 fxml 文件中,但它们的使用不是。
获取异常:
javafx.fxml.LoadException: MenuBarControl is not a valid type.
但是如果我看到 fxml 文件导入已更新但未使用。
< ? import q.A ? >
< ? import r.A ? >
<VBox fx:id="top">
<MenuBarControl fx:id="menuBarControl"/>
</VBox>