Maven Failsafe 因 java.lang.NoClassDefFoundError 失败

问题描述 投票:0回答:4

我开始了一个新项目:PostfixSQLConfig。这是一个简单的 Spring Boot 应用程序,本质上应该为 4 个简单的数据库表提供 CRUD 访问。我为第一个表编写了存储库,并为该存储库编写了一些基本的集成测试。由于这个特定的表不应该提供更新功能,我将更新功能实现为:

@Override
public void update(@NonNull Domain domain) throws NotUpdatableException {
    throw new NotUpdatableException("Domain entities are read-only");
}

其中

NotUpdatableException
是我的自定义异常类。

这段代码的 IT 看起来像这样:

@Test(expected = NotUpdatableException.class)
public void testUpdate() throws NotUpdatableException {
    val domain = Domain.of("test");

    domainRepository.update(domain);
}

如果从我的 IDE (IntelliJ 2018.2 EAP) 运行此测试,它会顺利通过,但运行

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

老实说我不知道为什么......

有人遇到过这个问题吗?

java maven spring-boot junit4 maven-failsafe-plugin
4个回答
24
投票

我想通了,所以我正在回答我自己的问题,以防其他人遇到同样的问题。

事实证明,maven-failsafe-plugin 不会将 target/classes 目录添加到类路径中,而是添加生成的 jar,在大多数情况下都可以正常工作。

然而,当涉及到 Spring Boot 时,生成的 jar 包含 Spring Boot 自定义类加载器类来代替 target/classes 目录的内容,这些内容被移动到目录 BOOT-INF/classes 中。由于 maven-failsafe-plugin 使用“常规”类加载器,因此它仅加载 Spring Boot 类加载器类,因此首先会失败,预计会使用其中一个项目类。

要在 Spring Boot 项目中运行 IT 测试,必须从依赖项中排除打包的 jar,并添加原始的、未修改的 jar 或 target/classes 目录,这就是我所做的。

maven-failsafe-plugin 和 Spring Boot 的正确配置是:

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

4
投票
另一个似乎有效的选项是将分类器添加到 spring-boot-maven-plugin 配置中。这会导致 SpringBoot 单独保留“默认”构建目标 jar,而是创建附加了分类器名称的 SpringBoot uber jar。

<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>sb-executable</classifier> </configuration> </plugin>
    

2
投票
这对我在 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

谢谢!


0
投票
SpringBoot 既不能加载类,也不能加载

application.properties

。所以在我解决了
NoClassDefFoundError
之后,它的属性失败了。这是我的配置,希望有帮助。

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.2.5</version> <configuration> <classpathDependencyExcludes> <classpathDependencyExcludes>${groupId}:${artifactId}</classpathDependencyExcludes> </classpathDependencyExcludes> <additionalClasspathElements> <additionalClasspathElement>${project.build.outputDirectory}</additionalClasspathElement> </additionalClasspathElements> <systemPropertiesFile>${project.build.outputDirectory}/application.properties</systemPropertiesFile> </configuration> <dependencies> <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit47</artifactId> <version>3.2.5</version> </dependency> </dependencies> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> </execution> </executions> </plugin> </plugins>
    
© www.soinside.com 2019 - 2024. All rights reserved.