【问题标题】:Can I use the classpath to override a file in a jar that is being run?我可以使用类路径覆盖正在运行的 jar 中的文件吗?
【发布时间】:2012-02-09 05:53:49
【问题描述】:

我有一个 JAR 文件,其中包含一个应用程序以及该应用程序的配置文件。应用程序从类路径加载配置文件(使用ClassLoader.getResource()),并使用烘焙到 JAR 文件中的配置文件完全满足其依赖关系。

有时我希望应用程序以稍微不同的配置运行(特别是我想覆盖 JDBC URL 以指向不同的数据库),因此我创建了一个新的配置文件,将其存储在正确的目录结构中(其中表示在类路径条目的目录/config 中),我想做这样的事情:

java -cp new-config:. -jar application.jar

但我无法让类路径在应用程序 JAR 的内容之前有 new-config 路径条目。 JAR 的内容始终是类路径中的第一个内容是硬编码的吗?

【问题讨论】:

  • 您是否尝试过将配置放在 de jar 之外,放在相对于 application.jar (../conf/config.jar) 的路径中的自己的 jar 文件中?如果您这样做,我认为您可以在应用程序清单中设置指向该配置 jar 的类路径,并且可以设置更改 config.jar 的新配置。我希望我有更多的时间做一个演示来确认我的答案,但我不能......所以我把它写成评论
  • 你的意思是,而不是在 JAR 中?
  • 是的,而不是与应用程序放在同一个 jar 中,而是将配置放在第二个 jar 中...
  • 谢谢,但这并不真正适用于我的使用 - 我尝试支持的用户使用构建脚本在他们的工作站上创建 JAR,然后手动将其传输到服务器。要求他们传输两个文件将是一个问题。
  • 这是一个已知的BUG,目前还没有修复,不能结合-cp和-jar:bugs.sun.com/bugdatabase/view_bug.do?bug_id=4459663

标签: java jar classpath executable-jar


【解决方案1】:

为什么不直接调用应用程序而不指定 -jar 而是显式命名应用程序主类?这将允许您将 new-config 和 application.jar 按所需顺序放在类路径中:

例如(假设“new-config”是一个包含被覆盖的属性文件的目录)

java -cp new-config:application.jar Application.Main.Class

我相信主类的名字可以在jar里面的MANIFEST.MF文件中找到....

【讨论】:

  • 主要问题是我实际上并没有使用 -cp 参数,而是在清单文件中指定类路径,因为应用程序需要许多其他外部 JAR - 提取所有这些将很容易出错。但我敢打赌,我可以编写一些脚本,从清单中提取类路径并自动构建相关的命令行,所以这可能是我将使用的答案。
【解决方案2】:

当您使用 -jar 选项启动应用程序时:

... JAR 文件是所有文件的来源 用户类和其他用户类 路径设置被忽略

here 所述。一种解决方法是在 jar 文件的清单中指定类路径以包含附加路径(描述为 here)。

但是,鉴于您只是在讨论修改配置,您可能希望采用不依赖于类路径的不同方法。例如,我通常使用属性文件通过 Spring 配置我的应用程序以确定数据库的位置等。我的 Spring 配置在测试、QA 和实时环境中是一致的,但我在启动应用程序时将不同的属性文件作为命令行参数传递.

Spring 配置片段

<bean id="MyDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
    <property name="url" value="jdbc:microsoft:sqlserver://${dbServer}:${dbPort};DatabaseName=${dbName}"/>
    <property name="username" value="${dbUserName}"/>
    <property name="password" value="${dbPassword}"/>
    <property name="suppressClose" value="false"/>
</bean>

属性文件片段

dbServer=MyServer
dbPort=1433
dbName=MyDb
dbUserName=Me
dbPassword=foobar

【讨论】:

  • 将配置文件作为可选参数传递是个好主意,但我的应用程序目前接受相当多的参数,添加另一个参数会给用户带来问题。不过感谢您的回答。
  • 如何启动您的应用程序?我原以为通过 Webstart、.bat 或 .sh 脚本等启动应用程序,传递的参数会对用户隐藏。
  • 否 - 用户自己从命令行启动应用程序,传入所需的参数 - 日期、要处理的文件等。
  • 我猜你总是可以让用户通过脚本或 bat 文件启动应用程序,该脚本或 bat 文件接受 JVM 参数或参数并将它们与属性文件一起传递给 JVM?如果不了解用户的更多背景信息等,很难判断这是否是最佳选择。
  • 最终,我们采用了更复杂的实现,将加载器逻辑更改为首先查看当前工作目录的几个文件夹,然后再返回到类路径加载语义。
【解决方案3】:

-jar 选项指定的 JAR 存档覆盖所有其他值。

您通常必须使用外部配置文件或使用 ClassLoader.getResource() 构建自己的解决方案。

我们使用自定义解决方案来解决这个问题 - 我们像这样加载内部属性:

final Properties p = new Properties();
p.load(DefaultConfiguration.class.getResourceAsStream("config.properties"));

然后我们以相同的方式加载外部文件并用外部值覆盖内部值。

有关类加载如何工作的信息,请参阅:

http://java.sun.com/javase/6/docs/technotes/tools/findingclasses.html

【讨论】:

  • 确实是这个问题。我知道除了按照 alasdairg 的建议以不同的方式启动应用程序或编写一些自定义加载代码之外,没有其他办法。谢谢。
【解决方案4】:

仅使用 CLASSPATH 可能是不可能的。有一些方法可以调用ClassLoader.getResource() 使用静态路径来查找资源。如果它这样做,它就是绕过 CLASSPATH。

【讨论】:

    猜你喜欢
    • 2018-11-05
    • 2013-12-17
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    相关资源
    最近更新 更多