Maven Failsafe 失败 java.lang.NoClassDefFoundError
Maven Failsafe fails with java.lang.NoClassDefFoundError
我开始了一个新项目:PostfixSQLConfig。这是一个简单的 Spring 引导应用程序,本质上应该为 4 个简单的数据库 table 提供 CRUD 访问。我为第一个 table 编写了存储库,并为所述存储库编写了一些基本的集成测试。由于这个特定的 table 不应该提供更新功能,我将更新功能实现为:
@Override
public void update(@NonNull Domain domain) throws NotUpdatableException {
throw new NotUpdatableException("Domain entities are read-only");
}
其中 NotUpdatableException
是我的自定义异常 class。
此代码的 IT 如下所示:
@Test(expected = NotUpdatableException.class)
public void testUpdate() throws NotUpdatableException {
val domain = Domain.of("test");
domainRepository.update(domain);
}
如果 运行 这个来自我的 IDE (IntelliJ 2018.2 EAP) 的测试它通过了很好,但是 运行ning mvn verify
失败了:
java.lang.NoClassDefFoundError: com/github/forinil/psc/exception/NotUpdatableException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at org.apache.maven.surefire.util.ReflectionUtils.tryGetMethod(ReflectionUtils.java:60)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.isSuiteOnly(JUnit3TestChecker.java:65)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.isValidJUnit3Test(JUnit3TestChecker.java:60)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.accept(JUnit3TestChecker.java:55)
at org.apache.maven.surefire.common.junit4.JUnit4TestChecker.accept(JUnit4TestChecker.java:53)
at org.apache.maven.surefire.util.DefaultScanResult.applyFilter(DefaultScanResult.java:102)
at org.apache.maven.surefire.junit4.JUnit4Provider.scanClassPath(JUnit4Provider.java:309)
at org.apache.maven.surefire.junit4.JUnit4Provider.setTestsToRun(JUnit4Provider.java:189)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:132)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.ClassNotFoundException:
com.github.forinil.psc.exception.NotUpdatableException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 18 more
老实说我不知道为什么...
有人遇到过这个问题吗?
我弄明白了,所以我正在回答我自己的问题,以防其他人遇到同样的问题。
事实证明,maven-failsafe-plugin 不会将 target/classes 目录添加到类路径中,而是将生成的 jar 添加到类路径中,这在大多数情况下都可以正常工作。
当谈到 Spring 引导时,生成的 jar 包含 Spring 引导自定义类加载器 类 代替 target/classes 目录的内容,这些内容被移动到目录 BOOT-INF/classes。由于 maven-failsafe-plugin 使用 'regular' 类加载器,它只加载 Spring 引导类加载器 类,首先失败,它应该使用项目之一 类.
要在 Spring 引导项目中进行 运行 IT 测试,必须从依赖项中排除打包的 jar 并添加原始的、未修改的 jar 或 target/classes 目录,这就是我做到了。
maven-failsafe-plugin 和 Spring 引导的正确配置是:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.21.0</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<classpathDependencyExcludes>
<classpathDependencyExcludes>${groupId}:${artifactId}</classpathDependencyExcludes>
</classpathDependencyExcludes>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.outputDirectory}</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
另一个似乎可行的选项是向 spring-boot-maven-plugin 配置添加分类器。这会导致 SpringBoot 单独保留 "default" 构建目标 jar,而是创建附加了分类器名称的 SpringBoot uber jar。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>sb-executable</classifier>
</configuration>
</plugin>
对于 spring-boot 项目和 failsafe plugin version - 3.0.0-M5
这对我有用
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-failsafe-plugin</artifactid>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<classesdirectory>${project.build.outputDirectory}</classesdirectory>
</configuration>
</plugin>
博客引用/漂亮的解释 - https://bjoernkw.com/2020/12/06/using-maven-failsafe-with-spring-boot/
如果您想了解什么是 project.build.outputDirectory - Maven project.build.directory
谢谢!
我开始了一个新项目:PostfixSQLConfig。这是一个简单的 Spring 引导应用程序,本质上应该为 4 个简单的数据库 table 提供 CRUD 访问。我为第一个 table 编写了存储库,并为所述存储库编写了一些基本的集成测试。由于这个特定的 table 不应该提供更新功能,我将更新功能实现为:
@Override
public void update(@NonNull Domain domain) throws NotUpdatableException {
throw new NotUpdatableException("Domain entities are read-only");
}
其中 NotUpdatableException
是我的自定义异常 class。
此代码的 IT 如下所示:
@Test(expected = NotUpdatableException.class)
public void testUpdate() throws NotUpdatableException {
val domain = Domain.of("test");
domainRepository.update(domain);
}
如果 运行 这个来自我的 IDE (IntelliJ 2018.2 EAP) 的测试它通过了很好,但是 运行ning mvn verify
失败了:
java.lang.NoClassDefFoundError: com/github/forinil/psc/exception/NotUpdatableException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at org.apache.maven.surefire.util.ReflectionUtils.tryGetMethod(ReflectionUtils.java:60)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.isSuiteOnly(JUnit3TestChecker.java:65)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.isValidJUnit3Test(JUnit3TestChecker.java:60)
at org.apache.maven.surefire.common.junit3.JUnit3TestChecker.accept(JUnit3TestChecker.java:55)
at org.apache.maven.surefire.common.junit4.JUnit4TestChecker.accept(JUnit4TestChecker.java:53)
at org.apache.maven.surefire.util.DefaultScanResult.applyFilter(DefaultScanResult.java:102)
at org.apache.maven.surefire.junit4.JUnit4Provider.scanClassPath(JUnit4Provider.java:309)
at org.apache.maven.surefire.junit4.JUnit4Provider.setTestsToRun(JUnit4Provider.java:189)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:132)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.ClassNotFoundException:
com.github.forinil.psc.exception.NotUpdatableException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 18 more
老实说我不知道为什么...
有人遇到过这个问题吗?
我弄明白了,所以我正在回答我自己的问题,以防其他人遇到同样的问题。
事实证明,maven-failsafe-plugin 不会将 target/classes 目录添加到类路径中,而是将生成的 jar 添加到类路径中,这在大多数情况下都可以正常工作。
当谈到 Spring 引导时,生成的 jar 包含 Spring 引导自定义类加载器 类 代替 target/classes 目录的内容,这些内容被移动到目录 BOOT-INF/classes。由于 maven-failsafe-plugin 使用 'regular' 类加载器,它只加载 Spring 引导类加载器 类,首先失败,它应该使用项目之一 类.
要在 Spring 引导项目中进行 运行 IT 测试,必须从依赖项中排除打包的 jar 并添加原始的、未修改的 jar 或 target/classes 目录,这就是我做到了。
maven-failsafe-plugin 和 Spring 引导的正确配置是:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.21.0</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<classpathDependencyExcludes>
<classpathDependencyExcludes>${groupId}:${artifactId}</classpathDependencyExcludes>
</classpathDependencyExcludes>
<additionalClasspathElements>
<additionalClasspathElement>${project.build.outputDirectory}</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
另一个似乎可行的选项是向 spring-boot-maven-plugin 配置添加分类器。这会导致 SpringBoot 单独保留 "default" 构建目标 jar,而是创建附加了分类器名称的 SpringBoot uber jar。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>sb-executable</classifier>
</configuration>
</plugin>
对于 spring-boot 项目和 failsafe plugin version - 3.0.0-M5
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-failsafe-plugin</artifactid>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<classesdirectory>${project.build.outputDirectory}</classesdirectory>
</configuration>
</plugin>
博客引用/漂亮的解释 - https://bjoernkw.com/2020/12/06/using-maven-failsafe-with-spring-boot/
如果您想了解什么是 project.build.outputDirectory - Maven project.build.directory
谢谢!