在其他目录中编译和 运行 java 具有许多依赖关系的项目 类

Compiling and running java project with many dependencies classes in other directories

我所有目录的结构都是这样的:

我正在尝试编译和执行 ServerMain 和 ClientMain。以下编译有效:

src>javac -cp .;../lib/gson-2.8.2.jar client/gui/*.java client/net/*.java client/*.java server/*.java server/net/*.java server/exceptions/*.java server/controller/*.java server/data/*.java common/*.java

我必须不在src中执行程序,而是在之前的目录中执行程序,因为在代码中有一个从cfg目录中的配置文件读取的函数。

我尝试执行此命令,但出现很多错误:ClassesNotFound 或 cfg 文件路径错误(代码中为 /cfg/server.cfg)或未找到 ServerMain。

下面的命令是在src的上一个目录执行的,也就是images中的PROJECT

java -cp lib/gson-2.8.2.jar src/server/WinsomeServerMain

如何正确执行?

唯一真正正确的答案是:把所有这些东西扔进垃圾桶;去给自己一个构建系统,比如 maven or gradle.

为什么?

javac 并不是特别擅长在不同的目录中编译许多相互关联的源文件,然而,将所有代码集中到一个巨大的包中并不是一个好主意。这些工具是。你只是..告诉他们编译。他们 'know' java 源在 src/main/java 中,并且可以轻松调整以考虑 2 个单独的 'projects'(src/client/javasrc/server/java,在您的案例),甚至为这些提供单独的罐子。

还有依赖管理的问题:使用这些工具,你只需在配置文件中编写一个so-called GAV字符串(Group/Artifact/Version),例如com.google.gson::gson::2.9.0和maven/gradle 只是照顾它。它知道在哪里可以在线找到 gson 并为您下载它,并确保它自动位于所有相关的 class 路径上。这很好:将所有这些 dep 粘贴到您的源代码控制系统中意味着源代码控制的大小激增,这很烦人,所以如果不在源代码控制中,您如何在计算机/开发人员之间传输 gson?答案是:你不需要 - 每个需要它的系统都会自动下载它。哪个 maven 和 gradle 可以为您做。

最后,您免费获得'just compile ALL the files',您无需像现在这样列出6 个目录。真不错。

但是我真的很想用javac

你错了。但如果你必须:

您的代码段中的错误 1:src 滥用

您的 javac 命令编译所有各种文件 到位 ,这意味着,您现在在包含 [=18] 的文件夹结构中有 .class 个文件=] 现在你的项目结构在骗你。这没有用。如果您真的打算让 java 和 class 文件存在于同一个目录中,那么只需在层次结构中删除 src 即可。它应该只是 yourMainProjectDir/client/net/MyClient.java 和旁边的 MyClient.class。或者,使用 -d 开关告诉 javac 输出到特定目录。例如,bin:

javac -d bin -cp 'lib/*' client/gui/*.java ..and all other dirs here..

错误 2 - 文件名而不是完全限定的 class 名称

java 没有 运行 文件 。它 运行s classes。顺序参数(在您的示例中为 src/server/WinsomeServerMain)是 完全限定的 class 名称 。类似于 java.lang.String,而不是 java/lang/String.classjava 然后将扫描 class 路径中列出的每个目录( files/directories,并通过 -cp 传递)以获取该资源.

在你的情况下,你需要在 class 路径上有 2 个独立的东西:gson 和你自己的 class 文件,你刚刚编译。

因此:

java -cp 'lib/*:bin' server.WinsomeServerMain

这里发生了一些事情:

  • 其实我不知道你的包裹是什么,从你的问题中看不清楚。您应该在 WinsomeServerMain.java 文件的顶部有一个 package xyz; 语句。如果是server,上面就是'correct'。如果没有 package 语句,你需要一个,packageless 对除了它自己包中的其他东西之外的所有东西都是不可见的,并且在你引入任何类型的任何层次结构时应该放弃。

  • Java 可以处理 class 路径中的 *,但是您 必须 让 java 进行解包。在除了 plain jane windows 之外的几乎每个 shell 环境中,shell 都会看到 * 并为您解压它,您不希望这样,因此,您必须使用单引号来告诉 shell 不要对此采取行动。还有,java很简单,就是理解*。它不理解 *.jar,所以不要输入它。 class 路径根包含 class 路径。因此,bin 是一个根(因为 class 文件直接在其中),但 lib 不是(因为 class 文件不在 lib - 它们在一个 jar 文件中,jar 文件在 lib) 中。因此,这不是拼写错误:bin 是单独列出的,而是 lib/*。这意味着您可以将任何您想要的库放入 lib 并且 lib/* 将得到它们。

如何处理配置文件

如果这些配置文件是只读的('shipped with your app' - 永远不会改变并且你在编译时知道的静态数据),你应该将它们粘贴到 jar 文件中并使用 getResourceAsStream。如果它们是用户可配置的,它们不应该是 'with' 你的 class 文件 - class 文件是代码,代码最好放在 read-only 位置(至少,就代码而言)。这就是为什么在它们旁边粘贴可编辑文件不是一个好主意。

一个好的解决方案是在 jar 中发布模板版本(使用 YourClass.class.getResourceAsStream("/configtemplate.txt") 读取它们 - 如果它位于 jar 的根目录中,它将找到该文件。开始时,读取 Paths.get(System.getProperty("user.home"), "myapp.config") 和如果它丢失了,创建它,用模板填充它并告诉用户去编辑它。

写入用户的主目录(C:\Users\YourUserName windows、/home/youracct linux、/Users/youracct mac , 等),专用于此目的,绝对可写。

更好的部署

构建系统的另一个优势ms 是它们使创建好的部署更容易:您确实希望将文件放在指定其自己的 class 路径的 jar 文件中。构建工具可以做到这一点。您最好 'ship' 您的应用程序,以便有人最终得到:

C:\Program Files\YourApp\YourApp.jar
C:\Program Files\YourApp\lib\gson.jar

并且 运行 应用程序唯一需要的是在资源管理器中键入 java -jar YourApp.jar 或双击 YourApp.jar - 并且所有这些都与平台无关( windows 和 mac 上的交易相同)。你可以这样做——罐子可以指定它们自己的 class 路径,该路径是相对于罐子解析的。这涉及包含一个清单,其中包含密钥 Class-Path: lib/gson.jar。您可以手动完成(jar cvmf MyManifest.mf MyApp.jar client/ui/*.class client/*.class etcetera - 类似的东西,您可以在其中创建 MyManifest.mf 文本文件并在其中列出该密钥),但是这里有很多步骤,并且 gradle/maven全部自动化。