如何在命令行中运行WAR包中的类?

90

我有一个Java类,其中包含一个main方法,我曾经可以从命令行作为独立应用运行它,例如:

java -jar myjar.jar params

我需要重新打包代码以在apache下运行,将所有代码(包括旧jar中的入口点类)放入WAR文件中,以便轻松部署到Web服务器。

然而,我仍然希望能够从命令行运行它,代码没有改变且全部都在那里,只是我无法弄清楚如何运行它。

这是我尝试过的...

我假设WAR文件就像一个jar文件,所以

java -jar mywar.war params

在清单文件中没有定义主类,执行失败。

我手动将清单文件添加到war文件中并再次尝试,结果相同。

我注意到我的war文件中有一个名为META-INF的文件夹,其中包含一个manifest.mf文件,因此我向其中添加了一行声明我的主类,就像向普通清单文件添加一样……

Manifest-Version: 1.0
Main-Class: mypackage.MyEntryPointClass

这导致了noClassDefFoundError mypackage.MyEntryPointClass的错误,这算是一定程度上的进展。这让我认为它只是路径问题,所以我尝试了...

Manifest-Version: 1.0
Main-Class: WEB-INF.classes.mypackage.MyEntryPointClass

现在我收到了相同的错误,但是带有堆栈跟踪...

Exception in thread "main" java.lang.NoClassDefFoundError: WEB-INF/classes/mypackage/MyEntryPointClass (wrong name: mypackage/MyEntryPointClass)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)

我在谷歌上搜索了一段时间,但找不到任何回答我的问题的东西,我也阅读了这里的几个稍有不同的其他问题,所以我想发帖问一下。

Java 1.5,虽然我认为这应该没有什么区别。


1
没试过,不过在清单文件中添加一个“Class-Path”条目怎么样? - Dan
你有什么理由吗?为什么不尝试保留两个不同的程序集 - 一个用于Web,另一个作为独立应用程序? - unorsk
你尝试在 manifest.mf 中放置一个包含 WEB-INF/classes 的 classpath,然后将 Main-Class 保留为 mypackage.MyEntryPointClass 吗? - Murali VP
尝试了类路径的想法,但没有成功。 - Simon
@Andrew,我可以有两个不同的软件包,但当一个软件包的内容是另一个软件包内容的超集时,这似乎是浪费的。 - Simon
12个回答

65

类似于Richard Detsch的方法,但更易于理解(也适用于软件包)。

步骤1:解压缩WAR文件。

jar -xvf MyWar.war

步骤2:进入目录

cd WEB-INF

步骤 3:使用所有依赖项运行主程序

java -classpath "lib/*:classes/." my.packages.destination.FileToRun

我正在寻找的答案 - AdamSkywalker
这能在不解压缩的情况下完成吗?我运行我的程序的环境比这更加受限。 - Sridhar Sarnobat
9
针对 Windows 系统,将冒号 : 改为分号 ;,使得上述命令变为 java -classpath "lib/*;classes/." my.packages.destination.FileToRun。具体操作如下: - ksrb

56

你可以做Hudson(持续集成项目)所做的事情。 你下载一个war包,可以在tomcat中部署或使用它来执行。

java -jar hudson.war

因为它有一个内置的Jetty引擎,所以从命令行运行它会启动服务器。无论如何,通过查看Hudson的清单,我理解他们在归档的根目录中放置了一个主类。在您的情况下,您的war布局应如下所示:

在根目录下:

  • mypackage/MyEntryPointClass.class
  • WEB-INF/lib
  • WEB-INF/classes
  • META-INF/MANIFEST.MF

同时清单应包含以下行:

Main-Class: mypackage.MyEntryPointClass

请注意,mypackage/MyEntryPointClass.class 只能通过命令行访问,而 WEB-INF/classes 下的类只能从应用服务器访问。
希望对您有所帮助。

2
不会给这个答案点踩,因为它包含了一些好的信息。然而,它并没有回答原始问题“...如何从命令行运行一个独立的应用程序...” - Baruch Atta

10
如果您正在使用Maven,只需按照 maven-war-plugin 文档中关于 "如何创建一个包含Web应用程序中类的JAR文件?" 的说明进行操作:在插件的 <configuration> 中添加<attachClasses>true</attachClasses>
<project>
  ...
  <artifactId>mywebapp</artifactId>
  <version>1.0-SNAPSHOT</version>
  ...
  <build>
    <plugins>
      <plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
    <configuration>
      <attachClasses>true</attachClasses>
    </configuration>
      </plugin>
    </plugins>
  </build>
  ...
</project>

您将在target/文件夹中获得2个产品:
  • project.war本身
  • project-classes.jar,其中包含所有编译后的类
然后,您可以使用经典方法执行主类:java -cp target/project-classes.jar 'com.mycompany.MainClass' param1 param2

10

一个war是一个Web应用程序。如果您想要一个控制台/独立应用程序重用与Web应用程序相同的类,请考虑将共享类打包在jar中,然后将其放入WEB-INF/lib中。然后从命令行中使用该jar。 这样,您既可以获得控制台应用程序,又可以在Servlet中使用相同的类,而不必制作两个不同的软件包。 当war被解压时,这当然是正确的。


当然,但这并非总是可能的。有时您只有一个文件,想要快速运行主类。 - vikingsteve

8

要从部署的war文件中执行SomeClass.main(String [] args),请执行以下操作:

  1. 编写具有main方法的类SomeClass.java(例如:public static void main(String[] args) {...})。
  2. 部署您的WAR文件。
  3. cd /usr/local/<yourprojectsname>/tomcat/webapps/projectName/WEB-INF
  4. java -cp "lib/jar1.jar:lib/jar2.jar: ... :lib/jarn.jar" com.mypackage.SomeClass arg1 arg2 ... arg3

注意1:要查看类SomeOtherClass.class是否在/usr/tomcat/webapps/<projectName>/WEB-INF/lib中,请运行:

cd /usr/tomcat/webapps/projectName/WEB-INF/lib && 
find . -name '*.jar' | while read jarfile; do if jar tf "$jarfile" | grep SomeOtherClass.class; then echo "$jarfile"; fi; done 

注意2:将输出写入标准控制台,以便通过在控制台上打印语句查看主函数的实际效果。这被称为后门。

注意3:Bozhidar Bozhanov 上面的注释似乎是正确的。


1
这是技术上正确的,所以我给了它一个+1,但我不认为这个解决方案是一个好主意。它需要手动构建类路径,而且非常容易出错。 - JBCP
同意JBCP的观点。 但是我正在使用Netbeans,在那里你可以请求Java类的属性,它有一个Runtime Classpath条目,收集完整的绝对类路径(我的是4000个字符 :)) 这是将Web应用程序的一些内部转换为可编写脚本的类的不错方式。 - BxlSofty

7
在存档文件中定位类的规则是文件的包声明和存档文件内的文件位置必须匹配。由于您的类位于WEB-INF/classes中,所以它认为该类无法在当前上下文中运行。
您唯一能做到的就是重新打包war文件,使.class文件位于归档根目录下的mypackage目录中,而不是WEB-INF/classes目录中。但是,如果这样做,您将无法从任何web类中访问该文件。
如果您想在war和java命令行之外重用此类,请考虑构建一个可执行jar文件,然后将该jar文件放在war文件的WEB-INF/lib目录中。

6
Maven 项目中,您可以通过将 archiveClasses 设置为 true,使用 Maven War plugin 自动构建 jar。以下是示例。
<plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-war-plugin</artifactId>
       <configuration>
        <archiveClasses>true</archiveClasses>
       </configuration>
</plugin>

运行Maven清理安装 - Shondeslitch

2

如果您使用Spring Boot,最好的方法是:

1/ 创建一个ServletInitializer类,继承SpringBootServletInitializer类。使用configure方法运行您的应用程序类。

2/ 始终生成一个maven install WAR文件。

3/ 使用这个构件,您甚至可以:

    . start application from war file with java -jar file.war

    . put your war file in your favorite Web App server (like tomcat, ...)

0

根据维基百科的说法,使用WAR文件时,加载到类路径中的类位于“/WEB-INF/classes”和“/WEB-INF/lib”目录中。

您可以尝试将类的副本简单地放在zip文件的根文件系统上(这就是war/jar的内容)。但我不确定这是否有效。

您始终可以创建两个单独的文件。


将副本放在公共位置肯定很丑。 - Bozho

0
作为另一种选择,可以将REST服务包含在war文件中,通过URL触发应用程序逻辑。将war文件部署到任何您想要的Web/应用服务器上。
然后,您可以通过任何基于命令行的HTTP客户端(如Linux上的curl)启动应用程序。
缺点是:通常这些HTTP客户端在不同的操作系统上是不同的。这对许多情况来说并不是关键问题。您也可以在Windows上安装curl。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接