Play Framework结合Maven执行generate-sources阶段无限阻塞问题
问题背景
当前开发的Web应用技术栈如下:
- 后端:Play Framework 2.8.13、Scala、Maven
- 前端:React
项目通过Maven调用脚本完成前端构建,使用的exec-maven-plugin配置如下:
<plugin> <artifactId>exec-maven-plugin</artifactId> <groupId>org.codehaus.mojo</groupId> <executions> <execution> <id>npm-install</id> <phase>generate-sources</phase> <goals> <goal>exec</goal> </goals> <configuration> <executable>${project.basedir}/install-front.sh</executable> </configuration> </execution> </executions> </plugin>
故障现象
执行mvn play2:run启动后端服务时,源码生成流程会无限卡住无响应,控制台最后停在输出:
[INFO] --- scala-maven-plugin:4.5.4:script ...
测试发现,把插件绑定的生命周期阶段从generate-sources改成clean后,异常消失,流程可正常运行。但这个方案不可用:执行mvn release时默认跳过clean阶段,会直接导致前端资源未构建,编译失败。
排查解决方法
按以下顺序定位处理即可:
- 先排查脚本本身的阻塞问题
先给exec-maven-plugin加配置打开日志输出,把脚本的标准输出、错误流全部打印到控制台,避免因为输出缓存导致的“假卡住”,配置如下:
重点检查脚本里是否有需要人工交互的命令:比如npm安装私有包要求输入凭证、命令带了需要手动确认的参数,全部改成非交互模式,比如npm安装用<configuration> <executable>${project.basedir}/install-front.sh</executable> <redirectErrorStream>true</redirectErrorStream> </configuration>npm ci --no-audit --no-fund --yes,禁用所有交互提示。 - 解决生命周期阶段的锁冲突
卡死的核心原因是阶段绑定错误:generate-sources阶段scala-maven-plugin、play2-maven-plugin会同时执行源码扫描、路由生成操作,会锁定target目录和源码目录,此时exec插件调用的前端构建脚本如果读写这些目录,会和前两个插件形成文件锁互等,直接死锁。绑定到clean阶段不报错,是因为clean阶段这两个插件还没启动,不存在锁竞争。
不要把前端构建绑定到generate-sources阶段——这个阶段是给源码生成类插件预留的,前端构建属于静态资源处理,直接调整绑定阶段到prepare-package即可:- 这个阶段在编译完成之后、打包之前执行,
play2:run是开发态热加载命令,不会触发完整的package生命周期,不会执行到这个绑定的前端构建,自然不会卡死 mvn release执行打包、发布流程时,一定会走到prepare-package阶段,不会漏构建前端资源,完全满足发布要求
- 这个阶段在编译完成之后、打包之前执行,
- 路径配置校验
把前端构建的输出目录指定到Play框架专用的静态资源路径(比如public/或者target/web/),不要往src/main/scala、target/scala-*这类scala插件、play插件的扫描目录写临时文件,避免触发插件的无限重扫描逻辑。 - 开发态适配
本地开发用mvn play2:run时,单独开一个终端跑前端构建的watch模式做热更新即可,不需要把前端构建绑到开发启动流程里,反而能提升启动速度。
实测验证:调整插件绑定阶段到
prepare-package、给脚本加非交互参数、打开日志输出后,mvn play2:run启动无卡顿,mvn release执行时前端资源可正常构建,不会出现编译失败问题。
内容的提问来源于stack exchange,提问作者Luc Ebert
相关产品推荐
相关产品推荐

