在其他目录中编译和 运行 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/java
和 src/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.class
。 java
然后将扫描 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全部自动化。
我所有目录的结构都是这样的:
我正在尝试编译和执行 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/java
和 src/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.class
。 java
然后将扫描 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全部自动化。