Overview of this article
This paper demonstrates how to make a springboot application based on gradle into a docker image through actual combat. The relevant software version information is as follows:
- Operating system: macOS Big Sur 11.2.2
- JDK: 1.8.0_211
- gradle: 6.8.3
- docker: 20.10.5
- springboot: 2.4.4
New java project
In order to be closer to the actual project, the actual java project is a multi module parent-child structure:
- Create a new project named Java demo, and its build.gradle content is as follows:
import java.time.OffsetDateTime import java.time.format.DateTimeFormatter // Relevant settings used by gradle itself buildscript { // Warehouse repositories { // local mavenLocal() // Central warehouse mavenCentral() // Grand plugin maven { url 'https://plugins.gradle.org/m2/' } } // Variables used by sub modules ext { springBootVersion = '2.4.4' } } // plug-in unit plugins { id 'java' id 'java-library' // With this declaration, the sub module can use the org.springframework.boot plug-in without specifying the version, but apply=false means that the current module does not use this plug-in id 'org.springframework.boot' version "${springBootVersion}" apply false id 'io.spring.dependency-management' version '1.0.11.RELEASE' } // gradle wrapper specifies the version wrapper { gradleVersion = '6.8.3' } // Take the current time def buildTimeAndDate = OffsetDateTime.now() // Generate string variables based on time ext { projectVersion = project.version buildDate = DateTimeFormatter.ISO_LOCAL_DATE.format(buildTimeAndDate) buildTime = DateTimeFormatter.ofPattern('HH:mm:ss.SSSZ').format(buildTimeAndDate) } // Configuration for all projects, including root projects allprojects { group 'com.bolingcavalry' version '1.0-SNAPSHOT' apply plugin: 'java' apply plugin: 'idea' apply plugin: 'io.spring.dependency-management' // Make a closure for setting parameters, which will be used later by both compileJava and compileTestJava def compileSetUp = { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 options.encoding = 'UTF-8' options.compilerArgs = [ '-Xlint:all', '-Xlint:-processing' ] } compileJava (compileSetUp) compileTestJava (compileSetUp) // Copy LICENSE tasks.withType(Jar) { from(project.rootDir) { include 'LICENSE' into 'META-INF' } } // When generating the jar file, the contents of MANIFEST.MF are as follows jar { manifest { attributes( 'Created-By': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})".toString(), 'Built-By': 'travis', 'Build-Date': buildDate, 'Build-Time': buildTime, 'Built-OS': "${System.properties['os.name']}", 'Specification-Title': project.name, 'Specification-Version': project.version, 'Specification-Vendor': 'Will Zhao', 'Implementation-Title': project.name, 'Implementation-Version': project.version, 'Implementation-Vendor': 'Will Zhao' ) } } // Warehouse repositories { // local mavenLocal() // Central warehouse mavenCentral() // Grand plugin maven { url "https://plugins.gradle.org/m2/" } } } // Similar to maven's dependency management, the versions of all jar s are specified here, and the sub modules do not need to specify the version when relying allprojects { project -> buildscript { dependencyManagement { imports { mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBootVersion}" mavenBom "org.junit:junit-bom:5.7.0" } dependencies { dependency 'org.projectlombok:lombok:1.16.16' dependency 'org.apache.commons:commons-lang3:3.11' dependency 'commons-collections:commons-collections:3.2.2' dependency 'org.slf4j:slf4j-log4j12:1.7.30' } } } } // Coordinate information group 'com.bolingcavalry' version '1.0-SNAPSHOT'
- Create a new module named democlient as a second-party library. When providing services, the data structure and interface are placed here. The content of build.gradle is as follows:
plugins { id 'java-library' } // Sub module's own dependency dependencies { api 'org.projectlombok:lombok' // The annotation processor will not be passed. Modules that use lombok generated code need to declare the annotation processor themselves annotationProcessor 'org.projectlombok:lombok' // slf4j's package can only be used by itself. Do not inherit it to other projects, otherwise it is easy to conflict with other log packages implementation 'org.slf4j:slf4j-log4j12' testImplementation('org.junit.jupiter:junit-jupiter') } test { useJUnitPlatform() }
- Create a new module named demowebapp, which is a springboot application. Its build.gradle content is as follows:
plugins { id 'org.springframework.boot' } // After using the plug-in org.springframework.boot, the jar task will fail and can be replaced by bootJar bootJar { archiveBaseName = project.name archiveVersion = project.version manifest { attributes( 'Created-By': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})".toString(), 'Built-By': 'travis', 'Build-Date': buildDate, 'Build-Time': buildTime, 'Built-OS': "${System.properties['os.name']}", 'Specification-Title': project.name, 'Specification-Version': projectVersion, 'Specification-Vendor': 'Will Zhao', 'Implementation-Title': project.name, 'Implementation-Version': projectVersion, 'Implementation-Vendor': 'Will Zhao' ) } } // Sub module's own dependency dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' // Binary library dependency implementation project(':democlient') // The annotation processor will not be passed. Modules that use lombok generated code need to declare the annotation processor themselves annotationProcessor 'org.projectlombok:lombok' implementation 'commons-collections:commons-collections' implementation 'org.apache.commons:commons-lang3' testImplementation('org.junit.jupiter:junit-jupiter') } test { useJUnitPlatform() }
- The startup class in the demowebapp module is DemoWebAppApplication.java:
package com.bolingcavalry; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoWebAppApplication { public static void main(String[] args) { SpringApplication.run(DemoWebAppApplication.class, args); } }
- Add a controller class to verify whether the service is normal:
package com.bolingcavalry.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.Date; @RestController @Slf4j public class Hello { @RequestMapping(value = "/hello", method = RequestMethod.GET) public String hello() { log.info("execute hello"); return "hello " + new Date(); } }
- After the project is created, it can be seen that this is a very simple and typical spring boot project with parent-child structure;
Build image practice
- Create a Dockerfile in the demowebapp directory. It can be seen that it is very simple. It only specifies the account and group, and the files required to copy the image:
FROM openjdk:8-jdk-alpine # Add groups and users RUN addgroup -S spring && adduser -S spring -G spring # Specify the users and groups of process 1 when the container runs USER spring:spring # Specifies the source location of the mirrored content ARG DEPENDENCY=build/dependency # Copy content to mirror COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY ${DEPENDENCY}/META-INF /app/META-INF COPY ${DEPENDENCY}/BOOT-INF/classes /app # Specify start command ENTRYPOINT ["java","-cp","app:app/lib/*","com.bolingcavalry.DemoWebAppApplication"]
- After preparation, you can start to create the image. The first step is to compile and build the whole project. Execute the following command in the Java demo directory to compile and build the project:
chmod +x gradlew && ./gradlew build
- After successful programming, you need to extract the contents of the jar (that is, those files required by the COPY command in Dockerfile):
mkdir -p demowebapp/build/dependency \ && (cd demowebapp/build/dependency; jar -xf ../libs/*.jar)
- Go to demowebapp/build/dependency. The content is ready:

- Execute the following command to build an image. The tag of the image is the current month, day, hour, minute and second:
cd demowebapp \ && docker build \ -t bolingcavalry/demowebapp-docker:`date "+%Y%m%d%H%M%S"` .
- After the execution is completed, the console prompts as follows:
(base) zhaoqindeMBP:java-demo zhaoqin$ cd demowebapp \ > && docker build \ > -t bolingcavalry/demowebapp-docker:`date "+%Y%m%d%H%M%S"` . [+] Building 1.7s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.1s => => transferring dockerfile: 542B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/openjdk:8-jdk-alpine 0.0s => [1/5] FROM docker.io/library/openjdk:8-jdk-alpine 0.1s => [internal] load build context 0.8s => => transferring context: 20.05MB 0.7s => [2/5] RUN addgroup -S spring && adduser -S spring -G spring 1.1s => [3/5] COPY build/dependency/BOOT-INF/lib /app/lib 0.1s => [4/5] COPY build/dependency/META-INF /app/META-INF 0.0s => [5/5] COPY build/dependency/BOOT-INF/classes /app 0.0s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:765d7a01490aaf2bd301e1cee68b3dc90b22768a5e7c957cde280cc2215d2848 0.0s => => naming to docker.io/bolingcavalry/demowebapp-docker:20210406080915
- Check the local image. The newly created image is shown in the red box below:

verification
- Execute the following command to start the image (please modify the image name according to your actual situation):
docker run --rm -p 8080:8080 bolingcavalry/demowebapp-docker:20210406080915
- The browser accesses the address Java demo, as shown in the following figure, which can work normally:

reference material:
Official documentation: https://spring.io/guides/gs/spring-boot-docker/
- So far, the operation of making the springboot application built by gradle into a docker image has been completed. If you are making your own application into a docker image, I hope this article can give you some reference;