[英]Java project with Maven dependency compiles but fails at runtime
EDIT: This question should absolutely not be closed.编辑:这个问题绝对不应该被关闭。 I'm NOT asking how to create an executable jar.我不是在问如何创建一个可执行的 jar。 A jar doesn't need to be executable to be run from the terminal. jar 不需要可执行即可从终端运行。 For example, if I have have this code: package com.dogzilla.maven.quickstart;例如,如果我有这个代码:
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World" );
}
}
...and Maven builds it, it creates quickstart-0.0.1-SNAPSHOT.jar. ...和 Maven 构建它,它创建 quickstart-0.0.1-SNAPSHOT.jar。 Which is not, ahem, 'executable'.嗯,这不是“可执行的”。
I can run it from the terminal quite successfully with this:我可以用这个从终端非常成功地运行它:
java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App
The problem, as I have written below, is experienced when using an external dependency.正如我在下面所写的,在使用外部依赖项时会遇到这个问题。 </end edit> </结束编辑>
I have a simple Maven project in Eclipse (2020-6).我在 Eclipse (2020-6) 中有一个简单的 Maven 项目。 It was set up by doing the following in Eclipse:它是通过在 Eclipse 中执行以下操作来设置的:
1. File -> New -> Other... Maven -> Maven Project
2. Used the maven-archetype-quickstart archetype
Group ID: com.dogzilla.maven
Artifact ID: quickstart
Right click the pom file -> select Add Dependency -> enter:右键pom文件->select添加依赖->输入:
Group ID: com.google.code.gson
Artifact ID: gson
Version: 2.8.6
Which I verified on https://search.maven.org/我在https://search.maven.org/上进行了验证
Here's the POM file:这是POM文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dogzilla.maven</groupId>
<artifactId>quickstart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>quickstart</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Here's the source code:这是源代码:
package com.dogzilla.maven.quickstart;
import com.google.gson.Gson;
public class App
{
public static void main( String[] args )
{
Gson gson = new Gson();
System.out.println(gson.toJson("Hello World!") );
}
}
I then right-clicked on the POM file -> Run As -> Maven Build...然后我右键单击 POM 文件 -> 运行方式 -> Maven Build...
and here is the output:这是 output:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/eclipse/java/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/opt/eclipse/java/configuration/org.eclipse.osgi/5/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/eclipse/java/plugins/org.eclipse.m2e.maven.runtime.slf4j.simple_1.16.0.20200610-1735/jars/slf4j-simple-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [file:/opt/eclipse/java/configuration/org.eclipse.osgi/5/0/.cp/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
[INFO] Scanning for projects...
[INFO]
[INFO] -------------------< com.dogzilla.maven:quickstart >--------------------
[INFO] Building quickstart 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ quickstart ---
[INFO] Deleting /opt/workspace/eclipse/java/quickstart/target
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ quickstart ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/workspace/eclipse/java/quickstart/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ quickstart ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /opt/workspace/eclipse/java/quickstart/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ quickstart ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/workspace/eclipse/java/quickstart/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ quickstart ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /opt/workspace/eclipse/java/quickstart/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ quickstart ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.dogzilla.maven.quickstart.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.022 s - in com.dogzilla.maven.quickstart.AppTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ quickstart ---
[INFO] Building jar: /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.967 s
[INFO] Finished at: 2020-12-13T20:00:27-07:00
[INFO] ------------------------------------------------------------------------
But if I run:但是如果我运行:
java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App
It fails with:它失败了:
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/Gson
at com.dogzilla.maven.quickstart.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: com.google.gson.Gson
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
... 1 more
The thing is if I change System.out.println(gson.toJson("Hello World;") ).问题是如果我更改 System.out.println(gson.toJson("Hello World;") )。 to plain 'ol System.out;println("Hello World").到普通的 'ol System.out;println("Hello World")。 it works.有用。 So I know the porblem isn't with my java command or how I set the project up outside of the dependency getting resolved.所以我知道问题不在于我的 java 命令或者我如何在依赖项之外设置项目得到解决。
So the question is, I'm not sure how this is failing on the dependency.所以问题是,我不确定这在依赖项上是如何失败的。 I was under the impression Maven managed all that for you.我的印象是 Maven 为您管理了所有这些。 Why is this failing to run?为什么这无法运行?
The jar file that created from your project only contains classes that written by you.从您的项目创建的 jar 文件仅包含您编写的类。 If you need to run the application, you need to include all the necessary dependencies (either direct or transitive) in your classpath.如果您需要运行应用程序,则需要在类路径中包含所有必要的依赖项(直接的或传递的)。
If you need a single jar file that you can execute without other dependencies, Please follow this to create a fatjar.如果您需要一个可以在没有其他依赖项的情况下执行的 jar 文件,请按照此创建一个 fatjar。
Use the Maven plugin mentioned in comment https://stackoverflow.com/a/65486325/679858 or use the Maven Shade plugin described here: https://maven.apache.org/plugins/maven-shade-plugin/ Use the Maven plugin mentioned in comment https://stackoverflow.com/a/65486325/679858 or use the Maven Shade plugin described here: https://maven.apache.org/plugins/maven-shade-plugin/
Your question was "Why does it run from Eclipse and not from console?": If you run it from Eclipse then the Maven plugin in Eclipse knows the full runtime classpath and so it works. Your question was "Why does it run from Eclipse and not from console?": If you run it from Eclipse then the Maven plugin in Eclipse knows the full runtime classpath and so it works. If you run it in console with the created JAR file then that JAR file only contains the classes of your sources but not the transitive dependencies, eg Google GSON.如果您使用创建的 JAR 文件在控制台中运行它,则该 JAR 文件仅包含源的类,但不包含传递依赖项,例如 Google GSON。 But if you use the Maven Assembly plugin or the Maven Shade plugin then you can create a JAR file which contains all the transitive dependencies of your project.但是,如果您使用 Maven 程序集插件或 Maven 阴影插件,那么您可以创建一个 JAR 文件,其中包含项目的所有传递依赖项。 That jar could be executed like you wrote it in your example. jar 可以像您在示例中编写的那样执行。
You are not providing the path to the additional, transitive, dependencies of your project in the java command, which is why it fails.您没有在 java 命令中提供项目的附加、传递、依赖项的路径,这就是它失败的原因。
You are using maven to build your project, not to execute your class: this is not Maven purpose, although it can do it using maven-exec-plugin
.您正在使用 maven 来构建您的项目,而不是执行您的 class:这不是 Maven 的目的,尽管它可以使用maven-exec-plugin
来实现。 Eclipse is different: it uses Maven information (with m2e) and provides a context to run classes, context that includes dependencies. Eclipse 不同:它使用 Maven 信息(带有 m2e)并提供运行类的上下文,包括依赖项的上下文。
The option -cp
, explained in depth at Oracle Web Site: java (15) , accepts one or more path to list of directories, jar and zip to search for class files: The option -cp
, explained in depth at Oracle Web Site: java (15) , accepts one or more path to list of directories, jar and zip to search for class files:
--class-path classpath, -classpath classpath, or -cp classpath --class-path 类路径、-classpath 类路径或 -cp 类路径
A semicolon (;) separated list of directories, JAR archives, and ZIP archives to search for class files. Specifying classpath overrides any setting of the CLASSPATH environment variable. If the class path option isn't used and
classpath isn't set, then the user class path consists of the current directory (.).未设置类路径,则用户 class 路径由当前目录 (.) 组成。
As a special convenience, a class path element that contains a base name of an asterisk (*) is considered equivalent to specifying a
list of all the files in the directory with the extension.jar or.JAR.目录中所有扩展名为.jar 或.JAR 的文件的列表。 A Java program can't tell the difference between the two invocations. Java 程序无法区分两次调用之间的区别。 For example, if the directory mydir contains a.jar and b.JAR, then the class path element mydir/* is expanded to A.jar:b.JAR, except that the order of JAR files is unspecified. For example, if the directory mydir contains a.jar and b.JAR, then the class path element mydir/* is expanded to A.jar:b.JAR, except that the order of JAR files is unspecified. All.jar files in the specified directory, even hidden ones, are included in the list.指定目录下的所有.jar文件,即使是隐藏文件,都包含在列表中。 A class path entry consisting of an asterisk (*) expands to a list of all the jar files in the current directory.由星号 (*) 组成的 class 路径条目将扩展为当前目录中所有 jar 文件的列表。 The CLASSPATH environment variable, where defined, is similarly expanded.定义的 CLASSPATH 环境变量也同样被扩展。 Any class path wildcard expansion that occurs before the Java VM is started.在 Java VM 启动之前发生的任何 class 路径通配符扩展。 Java programs never see wildcards that aren't expanded except by querying the environment, such as by calling System.getenv("CLASSPATH"). Java 程序永远不会看到未扩展的通配符,除非通过查询环境,例如通过调用 System.getenv("CLASSPATH")。
So you need to fix your command to provides gson and any other compile/runtime/provided dependencies you may add later:因此,您需要修复您的命令以提供 gson 以及您以后可能添加的任何其他编译/运行时/提供的依赖项:
CLASSPATH=''
CLASSPATH+=";$HOME/.m2/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar"
CLASSPATH+=";/opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar"
CLASSPATH="${CLASSPATH:1}" # remove leading ';'
java -cp "${CLASSPATH}" com.dogzilla.maven.quickstart.App
The man says that you can use ;
男人说你可以用;
to separate entries;分开条目; that is true at least for Windows.至少对于 Windows 来说是这样。 However on other Linux based OS (which seems to are on due to /opt
being used), this can also be :
: that what appassembler does when writing CLASSPATH
.然而,在其他基于 Linux 的操作系统上(由于/opt
正在使用,这似乎是打开的),这也可以是:
: appassembler在编写CLASSPATH
时所做的事情。
Do note that you will have to adapt the path manually to retarget the actual location of gson.jar (I guessed from what maven does by default).请注意,您必须手动调整路径以重新定位 gson.jar 的实际位置(我从 maven 默认情况下猜测)。
You can also use appassembler or launch4j to do the trick for you.您还可以使用appassembler或launch4j为您解决问题。 You can also use build-classpath to build the classpath (it may also work from command line).您还可以使用build-classpath来构建类路径(它也可以从命令行工作)。
try remove gson from.m2 folder then run maven install again.尝试从.m2 文件夹中删除 gson 然后再次运行 maven 安装。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.