简体   繁体   English

生成的JAR会为主类抛出ClassNotFoundException

[英]Generated JAR throws ClassNotFoundException for main class

I'm using IntelliJ IDEA to create a JAR. 我正在使用IntelliJ IDEA创建一个JAR。 I selected "JAR from modules with dependencies" and "extract to the target JAR" for library JARs - the generated JAR looks just fine: 我为库JAR选择了“来自具有依赖项的模块中的JAR”和“提取到目标JAR”-生成的JAR看起来很好:

myJar.jar
  |
  +- META-INF
  |  +- MANIFEST.MF
  +- com
  |  +- my
  |     +- package
  |        +- Main.class
  +- some dependencies...

I checked twice: all the needed dependencies are present. 我检查了两次:所有必需的依赖项都存在。 The Main-Class field in MANIFEST.MF points to the correct main class ( com.my.package.Main in this example). MANIFEST.MFMain-Class字段指向正确的主类(在此示例中为com.my.package.Main )。 I've opened the file in my archive tool and inspected it using 我已经在存档工具中打开了文件,并使用对其进行了检查

jar tf myJar.jar

and both show that the classes needed are available (also Main.class ). 两者都表明所需的类可用(还有Main.class )。 I've left the Classpath field in IntelliJ's wizard empty, as I do not need any external libraries. 我不需要IntelliJ向导中的Classpath字段,因为我不需要任何外部库。 However, when I issue 但是,当我发出

java -jar myJar.jar

it throws the following exception: 它引发以下异常:

Error: could not find or load main class com.my.package.Main
Caused by: java.lang.ClassNotFoundException: com.my.package.Main

I've followed this guide by JetBrains , and IntelliJ automatically added all dependencies from the Maven pom.xml to be extracted into the JAR, which is correct. 通过JetBrains遵循了本指南 ,并且IntelliJ自动将Maven pom.xml所有依赖关系添加到JAR中,这是正确的。

Am I missing something I should configure here and how comes that this does not work? 我是否缺少我应在此处配置的内容,这怎么不起作用?

(Note: Generating a JAR from that project also does not work when I choose "copy to output folder and link via manifest" for the library JARs.) (注意:当我为库JAR选择“复制到输出文件夹并通过清单链接”时,从该项目生成JAR也不起作用。)

(Note 2: Using maven-assembly-plugin is sadly not an option since I referenced other IntelliJ workspace modules which would then not get included.) (注2:不幸的是,使用maven-assembly-plugin并不是一种选择,因为我引用了其他IntelliJ工作区模块,这些模块随后将不包含在内。)


Update: That it should work as-is shows also the following phenominum: When I unzip my JAR and do 更新:应该按原样工作还显示以下现象:当我解压缩JAR并执行

java -cp . com.my.package.Main

in the created directory, it works without any problems, which is strange given that Java refuses to load it... 在创建的目录中,它可以正常工作,考虑到Java拒绝加载它,这很奇怪。

In my case, my JAR did not work as expected due to some signed JAR dependencies. 就我而言,由于某些签名的JAR依赖项,我的JAR无法正常工作。

These JARs bring a signature and key file, which also gets extracted when you embed the signed JARs. 这些JAR带有签名和密钥文件,当您嵌入签名的JAR时也会提取它们。 There is basically no risk in removing them, but they are one of the many possible reasons your JAR may not work. 删除它们基本上没有风险,但是它们是您的JAR可能无法正常工作的众多原因之一。

How to remove such signatures from a JAR? 如何从JAR中删除此类签名?
I'm describing the following steps for Linux/Darwin, but I think that there is a similar way for Windows. 我正在描述Linux / Darwin的以下步骤,但是我认为Windows也有类似的方法。

  1. Unpack you JAR. 解压缩您的JAR。
    Since JARs are nothing more than simple ZIP archives, you can use unzip : 由于JAR只不过是简单的ZIP存档,因此您可以使用unzip

     mkdir temporaryDirectory unzip myJar.jar -d temporaryDirectory/ 

    The -d option is optional, but it helps keeping your directory structure clean because it sets the target directory. -d选项是可选的,但是因为它设置了目标目录,所以有助于保持目录结构的整洁。

  2. Locate the signature files. 找到签名文件。
    The signature files (or keys) are located in the META-INF/ directory, so change there: 签名文件(或密钥)位于META-INF/目录中,因此请在此处进行更改:

     cd temporaryDirectory/META-INF/ 

    Next, we need to find the troubling files. 接下来,我们需要找到麻烦的文件。 They have the file extensions .SF (for the signature) and .DSA for the key file: 它们具有文件扩展名.SF (用于签名)和.DSA作为密钥文件:

     ll | grep '.DSA\\|.SF' 
  3. Delete (or rename) the signature and key files. 删除(或重命名)签名和密钥文件。
    Renaming these files bares the benefit that you can restore them later (for whatever reason), deleting them also does the trick but is a bit more risky: 重命名这些文件几乎没有好处,您可以稍后再恢复它们(无论出于何种原因),删除它们也可以解决这个问题,但风险更高:

    • Deleting: 删除中:

       rm signature.DSA signature.SF # Either enter the name of the files # instead of 'signature' or use * to delete any: # rm *.DSA *.SF 
    • Renaming: 重命名:

       rename 's/\\.DSA$/.DSA.old/' * # Append ".old" to all .DSA files rename 's/\\.SF$/.SF.old/' * # Append ".old" to all .SF files 
  4. Repacking your JAR. 重新包装您的JAR。
    Still in temporaryDirectory/ , we can repack our JARs to make them work: 仍在temporaryDirectory/ ,我们可以重新打包我们的JAR使其工作:

     cd ../ # If you're still in temporaryDirectory/META-INF jar cfm ../myWorkingJar.jar ./META-INF/MANIFEST.MF -C ./ . 

    Explanation: 说明:

    • jar cfm
      jar is Java's built-in JAR builder. jar是Java的内置JAR构建器。 Invoking it with c means that we want to create a JAR, f stands for the output file (which we specify next) and m is for the MANIFEST.MF we want to use. c调用它意味着我们要创建一个JAR, f代表输出文件(我们在下面指定), m代表我们要使用的MANIFEST.MF If you omit m , jar will write an empty MANIFEST.MF to the JAR. 如果省略mjar会将一个空的MANIFEST.MF写入JAR。
    • ../myWorkingJar.jar
      This is the path we want to output our JAR to. 这是我们要将JAR输出到的路径。 It belongs to f which we specified earlier. 它属于我们先前指定的f
    • ./META-INF/MANIFEST.MF
      This is the manifest file we want to use. 这是我们要使用的清单文件。 It belongs to the m of cfm . 它属于cfmm
    • -C ./
      This means that our .class files are located in this directory ( . ). 这意味着我们的.class文件位于此目录( . )中。
    • . (last argument) (最后一个论点)
      This specifies that we want to add this directory to the JAR. 这指定我们要将此目录添加到JAR。

    If you want a detailed description of what jar is doing, you can issue the command from above with cvfm instead of cfm ( v stands for verbose). 如果要详细了解jar功能,可以使用cvfm而不是cfm从上面发出命令( v表示详细)。

  5. Verify that it works. 验证它是否有效。
    All set, now you can check that your JAR is working as intended by issuing 全部设置好了,现在您可以通过发出以下命令来检查您的JAR是否正常运行

     java -jar myWorkingJar.jar 
  6. Removing the temporary directory. 删除临时目录。
    Since you're finished repairing your JAR, you can safely delete the temporary directory we created (and/or the "broken" JAR). 由于您已完成对JAR的修复,因此可以安全地删除我们创建的临时目录(和/或“损坏的” JAR)。

I've created a simple BASH script which "automates" this process a little bit: 我创建了一个简单的bash脚本,其中“自动”这个过程中一点点

#!/bin/bash
JARNAME=myJar.jar          # enter your JAR's name here
OUT_JARNAME=myJar_out.jar  # enter your output JAR's name here

# Creating the directory, unpacking the JAR, entering META-INF/
mkdir temp
unzip $JARNAME -d temp
cd temp/META-INF

# Renaming the troublemakers.
rename 's/\.DSA$/.DSA.old/' *
rename 's/\.SF$/.SF.old/' *

# Reassembling the JAR
cd ../
jar cfm ../$OUT_JARNAME ./META-INF/MANIFEST.MF -C ./ .
cd ../

# Uncomment this line if you wish to delete the temp directory upon finish.
# rm -r temp

I hope this helps people also encountering this issue. 我希望这可以帮助人们也遇到这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM