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 实例。
我在 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 实例。