Maven Exec 插件,ClassLoader ClassNotFoundException

Maven Exec plugin, ClassLoader ClassNotFoundException

我在 Whosebug 上尝试了很多建议,并用设置和 pom.xml 的多种组合测试了 3 天,其中 none 有效。请帮忙

我从一个有很多依赖项的大项目开始,spring-boot、hibernate 等。然后我创建了另一个小控制台项目,它从大项目中导入和使用一些 classes项目。它们不是父子项目。我所做的就是将 <dependency/> 添加到子项目 pom,就像这样。 P.S。大项目有一个 <parent/> spring-boot-starter-parent 并使用 spring-boot-maven-plugin.

<dependency>
    <groupId>myGroup</groupId>
    <artifactId>bigProject</artifactId>
    <version>1.0.1</version>
</dependency>

这适用于带有 m2e 的 Eclipse,我只使用 "Run as" java 应用程序并且小项目有效。 但是我需要在我的 linux VM 上上传和 运行 这些项目,它不使用 GUI 和 Eclipse,只使用终端。

然后在阅读之后,我尝试使用 maven exec 插件来 运行 这个小项目。我的步骤: 1. 在大项目上做 mvn clean install,确认它出现在我的 /.m2 本地存储库中。 2. 运行 mvn compile 在小项目上 3. 运行 mvn exec:java 在小项目上 在第2步失败,小项目class中的那些import ... throw package xxx does not exist。 Maven 因编译错误而失败。

然后,我尽量简化问题。我创建了两个只有 1 class、myLib 和 HelloWorld 控制台应用程序的测试项目,然后我添加了 myLib 依赖项 (pom)。 HelloWorld 项目打印来自 myLib 包中 class 的消息。 运行 步骤 1 到 3。有效。

public class App 
{
    public static void main( String[] args )
    {
        //SystemLog from big project, does not work
        //SystemLog log = new SystemLog();
        //log.setValue("test value");
        //System.out.println(log.getValue());

        //CustomMessage from myLib, works fine      
        CustomMessage cm = new CustomMessage();
        cm.setTheMessage("custom message");
        System.out.println(cm.getTheMessage() );
        System.out.println(CustomMessage.defaultMsg);

    }
}
小项目

pom.xml

<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>test</groupId>
    <artifactId>dependOnOthers</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>dependOnOthers</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>test</groupId>
            <artifactId>myLibrary</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com</groupId>
            <artifactId>bigproject</artifactId>
            <version>1.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.5.0</version>
                <executions>
                    <execution>

                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <includePluginDependencies>true</includePluginDependencies>
                    <mainClass>test.dependOnOthers.App</mainClass>
                    <!-- <arguments> <argument>argument1</argument> </arguments> -->

                    <!-- <arguments> <argument>-classpath</argument> <argument>target/lib/bigproject-1.0.1.jar</argument> 
                        </arguments> -->
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

然后我添加对大项目的引用,尝试使用 HelloWorld 打印日志消息,它通过了步骤 2,mvn 编译,但是当我执行步骤 3 时,mvn exec:java,java.lang.ClassLoader.loadClass()在我新建一个 SystemLog() 实例的行上抛出 ClassNotFoundException,class 在大项目中定义。

总结: 目标:尝试 运行 使用 Maven exec 插件的控制台应用程序,它依赖于另一个安装在本地 repo /.m2 中的项目 SystemLog class 是一个带有休眠注释的模型 class。 1.小项目依赖大项目,mvn编译失败,包不存在。 2. HelloWorld 依赖于myLib,工作正常。 3. HelloWorld 依赖于大项目,在 运行 时失败并出现 ClassNotFoundException。

我有一个解决方法,知道是什么导致了问题,但不知道所有细节,希望有人对此发表评论,或详细说明答案。

导致此问题的是 spring-boot-maven-plugin,我使用 运行 spring-boot 嵌入式 tomcat 服务器,使用 mvn spring-boot:run。这个插件似乎改变了 .jar 文件,也许是清单?

解决方法是删除此插件,运行 mvn clean install,安装在本地存储库中的 .jar 将正常工作。 运行 mvn compile 关于小项目。然后把插件加回大工程,运行mvn spring-boot:run

我不知道插件更改了什么导致包不存在的 .jar,它是否更改了包名称?

我的解决方法很笨拙,如果有一种方法可以通过定义 maven 目标来选择有或没有 spring-boot-maven-plugin 编译,这样我就不需要为不同的构建更改 pom (with/without 插入)。那将是一个更好的解决方案。

有点晚了,但我 运行 出现了同样的错误,正在将项目从 1.5.3 迁移到 2.1.4

我找到了解决这个问题的方法。

Spring Boot在2.0.0+版本改变了jar结构,所以你不需要指定你的class为<mainClass>test.dependOnOthers.App</mainClass>,你需要将JarLauncher指向为主要入口点。

插件执行jar的时候,java会调用JarLauncher的main方法,然后会调用spring的几个代码,然后他读取manifest,调用class在 Start-Class: 中定义。在你的情况下 test.dependOnOthers.App.

<configuration>
<!-- Since version 2 of spring, the jar is packed in 
another way and has his main method here, don't worry in the MANIFEST file is described which file to boot -->
<mainClass>org.springframework.boot.loader.JarLauncher</mainClass>

Spring 1.0 的旧结构,是 class 真正的 java 方式,所以 java classloader 可以加载它而无需问题并参考 class 当它在 class 路径中时,使用版本 2.0.0+,他们将用户 class 文件和依赖项复制到 BOOT-INF 中,因此 java classloader 无法再加载它,您需要使用 Spring classloader 来获取 class 实例。