返回

Spring Boot Maven多模块:解决Java 9模块找不到问题

java

Spring Boot、Java 9 模块与 Maven 多模块项目:找不到兄弟模块

在 Spring Boot 项目中使用 Java 9 模块化(Jigsaw)时,结合 Maven 的多模块构建方式,可能会遇到一个棘手的问题:模块间的依赖关系没有正确建立,导致编译时无法找到兄弟模块。具体来说,当你试图构建某个依赖于其他兄弟模块的模块时,会报告 “package does not exist” 这样的错误,表明 Java 模块系统没有识别到正确的模块路径。这个问题源于 Java 9 模块化与 Maven 依赖管理的交互,需要仔细配置。

问题分析

错误的原因在于 Maven 构建过程中,Maven 会在编译时优先加载本地 .m2 仓库的 jar 包依赖,但 Java 9 模块化又依赖于 module-info.java 中定义的模块声明。如果没有明确告知 Java 模块系统从哪里加载编译输出的类文件,编译过程就无法识别到其他模块的 module-info.java 并正确地加载相关依赖。

解决方案

为了解决这个问题,需要在 Maven 的编译阶段设置模块路径,确保编译器能找到模块。以下提供两个可选方案,可以解决这一问题。

解决方案一:使用 maven-compiler-plugin 指定模块路径

最常见的解决方案是在 maven-compiler-plugin 中指定模块路径,这样可以让编译器识别多模块项目中各个模块的输出位置。

操作步骤:

  1. 修改父 POM 的 maven-compiler-plugin 配置。需要在插件配置中添加 compilerArgs ,将多模块项目结构中编译后的 target 目录添加到编译路径中。
  2. 针对JDK9或更新的版本,需要添加 --module-path 参数到编译器的配置参数中。

代码示例:

<build>
  <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.1</version>
           <configuration>
             <source>${java.version}</source>
             <target>${java.version}</target>
                <compilerArgs>
                   <arg>--module-path</arg>
                   <arg>${project.basedir}/api/target/classes;${project.basedir}/common/target/classes;${project.basedir}/service/target/classes</arg>
                 </compilerArgs>
          </configuration>
       </plugin>
    </plugins>
</build>
  • ${project.basedir} 指向的是父项目目录。
  • 这里直接列出每个模块的编译后输出路径,需要包含当前正在编译的模块自身。也可以使用占位符的方式,比如${project.basedir}/*/target/classes ,更具动态性和灵活性。
  • 多个模块路径用分号 (;) 分隔。

解决方案二:使用 maven-dependency-plugin 复制模块输出

另一个可行的方案是使用 maven-dependency-plugin,将其他模块的输出复制到当前模块的 target 目录下,以便 Java 模块系统找到依赖。这个方案相对较灵活,它无需手动添加所有模块的编译目录,但也需要在每次编译之前执行资源复制。

操作步骤:

  1. 在需要依赖其他模块的子模块中(本例中是 service 模块),添加 maven-dependency-plugin 配置。
  2. 配置插件,将 common 模块的输出 jar 包拷贝到 service 的 target 目录的 modules 文件夹下
  3. 还需要配置 maven-compiler-pluginmodulePath 参数,指定编译时从 target/modules 读取module。

代码示例:

service/pom.xml

 <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                  <execution>
                     <id>copy-modules</id>
                     <phase>process-sources</phase>
                       <goals>
                         <goal>copy-dependencies</goal>
                        </goals>
                      <configuration>
                       <includeArtifactIds>common</includeArtifactIds>
                       <outputDirectory>${project.build.directory}/modules</outputDirectory>
                    </configuration>
                 </execution>
             </executions>
            </plugin>

          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
             <configuration>
                 <source>${java.version}</source>
                 <target>${java.version}</target>
                 <compilerArgs>
                     <arg>--module-path</arg>
                     <arg>${project.build.directory}/modules</arg>
                  </compilerArgs>
             </configuration>
          </plugin>
        </plugins>
    </build>
  • process-sources 生命周期表示编译前处理,保证模块jar的复制先于编译发生。
  • 此插件会按照你的配置拷贝所需模块的 Jar 到你指定的输出路径。
  • maven-compiler-plugin 配置增加 module path指向module文件位置。
  • 如果有多模块依赖,需要为依赖添加 <includeArtifactIds> 配置项。

安全提示

使用上述方案,确保 Java 模块系统和 Maven 的构建过程同步进行。特别是第一种方案,需要定期检查 maven-compiler-plugin 配置,确认模块路径已经更新。使用 maven-dependency-plugin 的时候,注意 copy-dependencies 产生的Jar可能不包含module 信息,可以使用 <goal>copy</goal> 同时添加 -Dmodule-path 指向class 路径来获取所有信息,根据实际需求灵活选择。

总结

这两个方案都旨在桥接 Maven 多模块结构和 Java 模块系统,通过指定正确的编译路径,让编译器可以正确识别兄弟模块。根据项目的实际情况,可以选择一种最适合的解决方案。使用这些方法可以有效地解决 "Spring Boot, Java 9 modules and Maven multi-modules project: can't find sibling module" 的问题,使项目结构更清晰,并维护起来更容易。