简体   繁体   English

Maven Failsafe 因 java.lang.NoClassDefFoundError 失败

[英]Maven Failsafe fails with java.lang.NoClassDefFoundError

I've started a new project: PostfixSQLConfig .我开始了一个新项目: PostfixSQLConfig It's a simple Spring Boot application that is essentialy supposed to provide CRUD access for 4 simple databse tables.这是一个简单的 Spring Boot 应用程序,基本上应该为 4 个简单的数据库表提供 CRUD 访问。 I wrote the the repository for the first table and some basic integration tests for said repository.我为第一个表编写了存储库,并为该存储库编写了一些基本的集成测试。 Since this particular table should not provide update functionality, I implemented update function as:由于此特定表不应提供更新功能,因此我将更新功能实现为:

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

where NotUpdatableException is my custom exception class.其中NotUpdatableException是我的自定义异常类。

The IT for this code look like this:此代码的 IT 如下所示:

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

    domainRepository.update(domain);
}

If run this test from my IDE (IntelliJ 2018.2 EAP) it passes fine, but running mvn verify fails with:如果从我的 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

And I have honestly no idea why...老实说,我不知道为什么......

Has someone ever encountered this problem?有人遇到过这个问题吗?

I figured it out, so I'm answering my own quesiton in case someone else has the same problem.我想通了,所以我正在回答我自己的问题,以防其他人遇到同样的问题。

It turns out that maven-failsafe-plugin does not add target/classes directory to the classpath, but rather the resulting jar, which works fine in most cases.事实证明,maven-failsafe-plugin 不会将 target/classes 目录添加到类路径中,而是将生成的 jar 添加到大多数情况下。

When it comes to Spring Boot, however, the resulting jar contains Spring Boot custom classloader classes in place of contents of target/classes directory, which are moved to directory BOOT-INF/classes.然而,当涉及到 Spring Boot 时,生成的 jar 包含 Spring Boot 自定义类加载器类来代替 target/classes 目录的内容,这些目录被移动到目录 BOOT-INF/classes。 Since maven-failsafe-plugin uses 'regular' classloader it only loads Spring Boot classloader classes, failing in the first place it is expected to use one of the project classes.由于 maven-failsafe-plugin 使用“常规”类加载器,它只加载 Spring Boot 类加载器类,首先失败它应该使用项目类之一。

To run IT tests in Spring Boot project, one has to exclude the packaged jar from dependencies and add either the original, unmodified jar or target/classes directory, which is what I did.要在 Spring Boot 项目中运行 IT 测试,必须从依赖项中排除打包的 jar,并添加原始的、未修改的 jar 或 target/classes 目录,这就是我所做的。

The correct configuration for maven-failsafe-plugin and Spring Boot is: 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>

Another option that seems to work is to add a classifier to the spring-boot-maven-plugin configuration.另一个似乎有效的选项是在 spring-boot-maven-plugin 配置中添加一个分类器。 This causes SpringBoot to leave the "default" build target jar alone and instead create the SpringBoot uber jar with the classifier name appended.这会导致 SpringBoot 单独保留“默认”构建目标 jar,而是创建附加了分类器名称的 SpringBoot uber jar。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM