Gradle构建Spring Boot镜像报错:no main manifest attribute
解决Spring Boot Gradle项目Docker运行时“no main manifest attribute”错误
问题情况
我有一个基于Gradle的Spring Boot项目,在build.gradle中自定义了dockerFile任务生成Dockerfile,生成过程无异常,但运行镜像时出现错误:
no main manifest attribute, in /app/demo-0.0.1-SNAPSHOT-plain.jar
当前build.gradle内容
import com.bmuschko.gradle.docker.tasks.image.Dockerfile plugins { id 'java' id 'org.springframework.boot' version '2.7.6' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'com.bmuschko.docker-remote-api' version '6.6.1' apply false } group = 'ms10gradle2' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation "org.springframework.boot:spring-boot-starter-data-jpa:$jpaVersion" implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() } task printJpaVer{ print "JPA VERSION: ${jpaVersion}" } task printfiles { doLast { def files = "cmd.exe /c dir".execute().text.trim() println(files) project.getAllprojects().forEach(System.out::print) } } task printTasks{ project.getTasks().forEach{ println("task name: "+ it) } } task printTasksInsideSubModulesTrueOrFalse{ project.getAllTasks(false).entrySet().forEach(System.out::println) } task printTaskVersion{ project.getAllprojects() .forEach(p -> println p.name +"- "+p.getVersion()) print "================================================================" project.getAllprojects().forEach(p -> println p.getVersion()) } task dockerFile(type: Dockerfile) { destFile.set(project.file('Dockerfile')) from "alpine:$alpineVersion" runCommand 'apk add --no-cache openjdk11' copyFile "build/libs/${jar.archiveFileName.get()}", '/app/' workingDir '/app/' entryPoint 'java' defaultCommand '-jar', "/app/${jar.archiveFileName.get()}" }
生成的Dockerfile内容
FROM alpine:3.11.2 RUN apk add --no-cache openjdk11 COPY build/libs/demo-0.0.1-SNAPSHOT-plain.jar /app/ WORKDIR /app/ ENTRYPOINT ["java"] CMD ["-jar", "/app/demo-0.0.1-SNAPSHOT-plain.jar"]
问题原因
错误核心是:Dockerfile中引用的-plain.jar是Gradle java插件生成的普通Java归档文件,不包含Spring Boot启动必需的主清单属性(如Main-Class、Start-Class)。而Spring Boot插件会生成包含所有依赖、启动逻辑和完整清单的可执行jar,该jar不带-plain后缀。
解决方案
1. 替换jar引用为Spring Boot可执行jar
修改dockerFile任务,将jar.archiveFileName.get()替换为bootJar.archiveFileName.get(),同时添加任务依赖确保先构建可执行jar:
修改后的dockerFile任务
task dockerFile(type: Dockerfile) { // 依赖bootJar任务,保证先生成可执行jar dependsOn bootJar destFile.set(project.file('Dockerfile')) from "alpine:$alpineVersion" runCommand 'apk add --no-cache openjdk11' // 使用Spring Boot插件生成的可执行jar copyFile "build/libs/${bootJar.archiveFileName.get()}", '/app/' workingDir '/app/' entryPoint 'java' defaultCommand '-jar', "/app/${bootJar.archiveFileName.get()}" }
2. 验证生成的Dockerfile
修改后生成的Dockerfile会引用正确的可执行jar,示例:
FROM alpine:3.11.2 RUN apk add --no-cache openjdk11 COPY build/libs/demo-0.0.1-SNAPSHOT.jar /app/ WORKDIR /app/ ENTRYPOINT ["java"] CMD ["-jar", "/app/demo-0.0.1-SNAPSHOT.jar"]
3. 额外优化(可选)
可以显式指定可执行jar的固定名称,避免版本号变动时修改Dockerfile:
tasks.named('bootJar') { archiveFileName = "demo-app.jar" }
对应dockerFile任务中直接使用固定文件名:
copyFile "build/libs/demo-app.jar", '/app/' defaultCommand '-jar', "/app/demo-app.jar"
验证步骤
- 执行
./gradlew bootJar dockerFile构建可执行jar并生成Dockerfile - 构建Docker镜像:
docker build -t demo-app . - 运行镜像:
docker run -p 8080:8080 demo-app
此时镜像应能正常启动,不再出现主清单属性缺失的错误。
内容的提问来源于stack exchange,提问作者Hagelslag
相关产品推荐
相关产品推荐

