簡體   English   中英

具有 Maven 依賴項的 Java 項目編譯但在運行時失敗

[英]Java project with Maven dependency compiles but fails at runtime

編輯:這個問題絕對不應該被關閉。 我不是在問如何創建一個可執行的 jar。 jar 不需要可執行即可從終端運行。 例如,如果我有這個代碼:

public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World" );
    }
}

...和 Maven 構建它,它創建 quickstart-0.0.1-SNAPSHOT.jar。 嗯,這不是“可執行的”。

我可以用這個從終端非常成功地運行它:

java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App

正如我在下面所寫的,在使用外部依賴項時會遇到這個問題。 </結束編輯>

我在 Eclipse (2020-6) 中有一個簡單的 Maven 項目。 它是通過在 Eclipse 中執行以下操作來設置的:

1.  File -> New -> Other... Maven -> Maven Project
2.  Used the maven-archetype-quickstart archetype

    Group ID:  com.dogzilla.maven
    Artifact ID:  quickstart

右鍵pom文件->select添加依賴->輸入:

Group ID: com.google.code.gson
Artifact ID: gson
Version: 2.8.6

我在https://search.maven.org/上進行了驗證

這是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>

這是源代碼:

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!") );
    }
}

然后我右鍵單擊 POM 文件 -> 運行方式 -> Maven Build...

這是 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] ------------------------------------------------------------------------

但是如果我運行:

java -cp /opt/workspace/eclipse/java/quickstart/target/quickstart-0.0.1-SNAPSHOT.jar com.dogzilla.maven.quickstart.App

它失敗了:

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

問題是如果我更改 System.out.println(gson.toJson("Hello World;") )。 到普通的 'ol System.out;println("Hello World")。 有用。 所以我知道問題不在於我的 java 命令或者我如何在依賴項之外設置項目得到解決。

所以問題是,我不確定這在依賴項上是如何失敗的。 我的印象是 Maven 為您管理了所有這些。 為什么這無法運行?

從您的項目創建的 jar 文件僅包含您編寫的類。 如果您需要運行應用程序,則需要在類路徑中包含所有必要的依賴項(直接的或傳遞的)。

如果您需要一個可以在沒有其他依賴項的情況下執行的 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/

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. 如果您使用創建的 JAR 文件在控制台中運行它,則該 JAR 文件僅包含源的類,但不包含傳遞依賴項,例如 Google GSON。 但是,如果您使用 Maven 程序集插件或 Maven 陰影插件,那么您可以創建一個 JAR 文件,其中包含項目的所有傳遞依賴項。 jar 可以像您在示例中編寫的那樣執行。

您沒有在 java 命令中提供項目的附加、傳遞、依賴項的路徑,這就是它失敗的原因。

您正在使用 maven 來構建您的項目,而不是執行您的 class:這不是 Maven 的目的,盡管它可以使用maven-exec-plugin來實現。 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:

--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

未設置類路徑,則用戶 class 路徑由當前目錄 (.) 組成。

 As a special convenience, a class path element that contains a base name of an asterisk (*) is considered equivalent to specifying a

目錄中所有擴展名為.jar 或.JAR 的文件的列表。 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. 指定目錄下的所有.jar文件,即使是隱藏文件,都包含在列表中。 由星號 (*) 組成的 class 路徑條目將擴展為當前目錄中所有 jar 文件的列表。 定義的 CLASSPATH 環境變量也同樣被擴展。 在 Java VM 啟動之前發生的任何 class 路徑通配符擴展。 Java 程序永遠不會看到未擴展的通配符,除非通過查詢環境,例如通過調用 System.getenv("CLASSPATH")。

因此,您需要修復您的命令以提供 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

男人說你可以用; 分開條目; 至少對於 Windows 來說是這樣。 然而,在其他基於 Linux 的操作系統上(由於/opt正在使用,這似乎是打開的),這也可以是: : appassembler在編寫CLASSPATH時所做的事情。

請注意,您必須手動調整路徑以重新定位 gson.jar 的實際位置(我從 maven 默認情況下猜測)。

您還可以使用appassemblerlaunch4j為您解決問題。 您還可以使用build-classpath來構建類路徑(它也可以從命令行工作)。

嘗試從.m2 文件夾中刪除 gson 然后再次運行 maven 安裝。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM