编译并运行其他目录中具有许多依赖类的java项目

发布于 2025-01-17 02:52:19 字数 737 浏览 0 评论 0原文

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

在此处输入图像描述

我正在尝试编译并执行 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的上一级目录(图中的PROJECT)中执行的,

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

如何正确执行?

I have this structure of all my directories:

enter image description here

I am trying to compile and execute ServerMain and ClientMain. The following compilation worked:

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

I have to execute the program not in src, but in the previous directory, because in the code there is a function which read from configuration file in the cfg directory.

I tried this command to execute but I have a lot of errors: ClassesNotFound or Errors in the path of cfg file (which is /cfg/server.cfg in the code) or the ServerMain is not found.

The following command is executed in the previous directory of src which is PROJECT in the images

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

How do I execute properly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ぇ气 2025-01-24 02:52:19

唯一真正正确的答案是:把所有这些东西扔进垃圾桶;为自己建立一个构建系统,例如 mavengradle.

为什么?

javac 并不是特别擅长编译不同目录中的许多互连的源文件,但是,将所有代码集中到一个巨大的包中并不是一个好主意。这些工具是。你只需..告诉他们编译。他们“知道”java 源代码位于 src/main/java 中,并且可以轻松调整以考虑 2 个独立的“项目”(src/client/java>src/server/java,在你的情况下),甚至为这些提供单独的 jar。

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

最后,您可以免费获得“只需编译所有文件”,无需像现在一样列出 6 个目录。那很好。

但我真的想使用 javac

你错了。但是,如果您必须:

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

您的 javac 命令会编译所有不同的文件就地,这意味着您现在拥有.class 文件位于包含 src 的文件夹结构中,现在您的项目结构在欺骗您。这没有用。如果您确实想让 java 和类文件位于同一目录中,那么只需删除层次结构中的 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 - 文件名而不是完全限定的类名

java 不运行文件。它运行类。顺序参数(在您的示例中为 src/server/WinsomeServerMain)是一个完全限定的类名。类似java.lang.String的东西,而不是类似java/lang/String.class的东西。然后,java 将扫描类路径中列出的每个目录(它们是文件/目录,并通过 -cp 传递)以查找该资源。

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

因此:

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

这里发生了一些事情:

  • 我实际上不知道你的包裹是什么,从你的问题中不清楚。您的 WinsomeServerMain.java 文件顶部应该有一个 package xyz; 语句。如果是服务器,则上面的内容是“正确的”。如果没有 package 声明,你需要一个,无包对除了其自己包中的其他内容之外的所有内容都是不可见的,并且应该在引入任何类型的任何层次结构时放弃。

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

如何处理配置文件

如果这些配置文件是只读的(“随应用程序一起提供” - 永远不会改变并且在编译时知道的静态数据),则应该将它们粘贴在 jar 文件中并使用getResourceAsStream。如果它们旨在供用户配置,则它们不应该与您的类文件“一起” - 类文件是代码,并且代码最好放置在只读位置(至少就代码而言) 。这就是为什么将可编辑文件放在它们旁边是一个坏主意。

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

就是为此目的而设计的,并且绝对是可写的

这会写入用户的主目录(Windows 上的 C:\Users\YourUserName、Linux 上的 /home/youracct、Mac 上的 /Users/youracct 等),该目录

。构建系统的优点是它们使创建良好的部署变得更容易:您确实希望您的文件位于指定其自己的类路径的 jar 文件中,您最好希望“发布”您的应用程序,以便有人结束。补充:

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

运行应用程序所需的唯一操作是键入 java -jar YourApp.jar 或只需在资源管理器中双击 YourApp.jar - 所有这些都与平台无关( Windows 和 Mac 上的交易相同)。您可以这样做 - jar 可以指定自己的类路径,该类路径是相对于 jar 进行解析的。这涉及到包含一个清单,其中包含关键的类路径:lib/gson.jar。您可以手动完成此操作(jar cvmf MyManifest.mf MyApp.jar client/ui/*.class client/*.class etcetera - 类似的操作,您可以在其中创建 MyManifest.mf 文本文件并列出其中的密钥),但是这里有很多步骤,而 gradle/maven 可以自动执行这一切。

The only really correct answer is: Toss all this stuff in the bin; go get yourself a build system, such as maven or gradle.

Why?

javac isn't particularly good at compiling many interconnected source files in different directories, and yet, just lumping all your code into one gigantic package isn't a good idea. These tools are. You just.. tell them to compile. They 'know' that java sources are in src/main/java, and can be easily adjusted to consider 2 separate 'projects' (src/client/java and src/server/java, in your case), and even deliver separate jars for these.

There's also the issue of dependency management: With these tools you just write a so-called GAV string (Group/Artifact/Version), such as com.google.gson::gson::2.9.0 in a config file and maven/gradle just takes care of it. It knows where to find gson online and downloads it for you, and ensures it's on all the relevant classpaths automatically. This is nice: Sticking all those deps in your source control system means the source control size skyrockets which is annoying, so if not in source control, how do you transfer gson between computers / developers? The answer is: You don't - every system that needs it just downloads it automatically. Which maven and gradle can do for you.

Finally, you get 'just compile ALL the files' for free, you don't need to list 6 directories, as you are now. That's nice.

But I really want to use javac

You'd be wrong. But if you must:

Error 1 in your snippets: src abuse

Your javac command compiles all the various files in place, meaning, you now have .class files in a folder structure that includes src and now your project structure is lying to you. This isn't useful. If you really intend for java and class files to live in the same dir, then just get rid of src in the hierarchy. It shuold then just be yourMainProjectDir/client/net/MyClient.java and next to it, MyClient.class. Alternatively, use the -d switch to tell javac to output to a specific directory. For example, bin:

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

Error 2 - filename instead of a fully qualified class name

java does not run files. It runs classes. The sequential argument (which in your example was src/server/WinsomeServerMain) is a fully qualified classname. Something like java.lang.String, not something like java/lang/String.class. java will then scan each directory listed in the classpath (which ARE files/directories, and passed with -cp) for that resource.

In your case, you need 2 separate things on the classpath: gson, and your own class files, which you just compiled.

Thus:

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

A few things are happening here:

  • I don't actually know what your packages are, it's not clear from your question. You should have a package xyz; statement at the top of your WinsomeServerMain.java file. If it's server, the above is 'correct'. If there is no package statement, you need one, packageless is invisible to everything except other stuff in its own package and should be abandoned the moment you introduce any hierarchy of any sort.

  • Java can handle * in classpaths, but you must let java do the unpacking. On just about every shell environment except plain jane windows, the shell will see that * and unpack it for you, you don't want that, hence, you MUST use single quotes to tell the shell not to act on it. Also, java is very simplistic and just understands *. It does not understand *.jar, so don't type that. A classpath root contains classpaths. Hence, bin is a root (as the class files are directly in it), but lib is not (because the classfiles aren't in lib - they are in a jar file, and the jar file is in lib). Hence, that isn't a typo: bin is listed on its own, but it's lib/*. This means you can put whatever libraries you want in lib and lib/* will get them all.

How to deal with config files

If those config files are intended to be read only ('shipped with your app' - static data that never changes and that you know at compile time), you should stick them in the jar file and read them with getResourceAsStream. If they are intended to be user configurable, they shouldn't be 'with' your class files - class files are code, and code should optimally be placed in a read-only location (at least, as far as the code is concerned). That is why sticking editable files next to them is a bad idea.

A good solution is to ship templated versions inside the jar (read them with YourClass.class.getResourceAsStream("/configtemplate.txt") - which will find that file if it's in the root of the jar. On start, read Paths.get(System.getProperty("user.home"), "myapp.config") and if it is missing, create it, filling it with the template and telling the user to go edit it.

That writes to a user's home dir (C:\Users\YourUserName on windows, /home/youracct on linux, /Users/youracct on mac, etc), which is intended for this purpose, and is definitely writable.

Better deployment

Yet another advantage of build systems is that they make creation of nice deploys easier: You really want your files in a jar file that specifies its own classpath. build tools can do that. You optimally want to 'ship' your app such that someone ends up with:

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

and that the only thing that is needed to run the app is to type java -jar YourApp.jar or just doubleclick YourApp.jar in the explorer - and for all that to be platform agnostic (same deal on windows and macs). You can do that - jars can specify their own classpath which is resolved relative to the jar. This involves including a manifest that here would have key Class-Path: lib/gson.jar inside it. You can do it by hand (jar cvmf MyManifest.mf MyApp.jar client/ui/*.class client/*.class etcetera - something like that, where you create the MyManifest.mf text file and list that key inside it), but there are so many steps here, and gradle/maven automate it all.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文