Why Gradle?

Gradle is a general purpose build tool. You can use it for any languages not just those that runs on the JVM. Its core model is based on tasks. The build essentially configures a set of tasks that gets wired together based on their dependencies. However, this does not mean that there is a lot of work to do. In Gradle, it is easy to build common types of project as common conventions and functionality are provided through plugins.

Add Spring Dependencies And Plugins

plugins {
    java
    id("org.springframework.boot") version "2.6.3"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

When applying the java plugin together with the org.springframework.boot plugin, a task for building an executable jar is automatically configured.

To manage dependencies in our Spring Boot project, we will apply the io.spring.dependency-management plugin, this allows us to control the versions of the dependencies it manages just by changing the properties.

extra["slf4j.version"] = "1.7.35"

Then, we add spring boot starter dependencies. Notice that we do so without including the version number. This means that we will use the same version of the dependency management plugin as Spring Boot uses.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-validation")

    developmentOnly("org.springframework.boot:spring-boot-devtools")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")

    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")

    runtimeOnly("com.h2database:h2")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Add OpenAPI Generator Plugin And Dependencies

The OpenAPI Generator Plugin will configure a openApiGenerate task which will generate spring controllers and model classes from an Open API specification written in json or yaml.

plugins {
    ....
    id("org.openapi.generator") version "5.4.0"
}

....
dependencies {
    ....
    implementation("io.swagger.core.v3:swagger-annotations:2.1.12")
    ....
}

We can then configure the openApiGenerate task. You can read more details about the available options here.

openApiGenerate {
    generatorName.set("spring")
    inputSpec.set("$projectDir/src/main/resources/api-swagger.yaml")
    outputDir.set("$buildDir/generated/openapi")
    apiPackage.set("io.thewokecoder.example.api")
    modelPackage.set("io.thewokecoder.example.model")
    configOptions.set(
        mapOf(
            "dateLibrary" to "java8",
            "generateApis" to "true",
            "generateApiTests" to "false",
            "generateModels" to "true",
            "generateModelTests" to "false",
            "generateModelDocumentation" to "false",
            "generateSupportingFiles" to "false",
            "hideGenerationTimestamp" to "true",
            "interfaceOnly" to "true",
            "library" to "spring-boot",
            "serializableModel" to "true",
            "useBeanValidation" to "true",
            "useTags" to "true",
            "implicitHeaders" to "true",
            "openApiNullable" to "false",
            "oas3" to "true"
        )
    )
}

Add Generated Source Files To Source Sets

....
sourceSets {
    main {
        java {
            srcDirs("$buildDir/generated/openapi/src/main/java")
        }
    }
}
....

Gradle will now recognize the generated output and package them together during build.

Generate Controller And Model Classes During Build

We add the following code which tells gradle that compileJava task depends on openApiGenerate task. Our source files will be generated during build time.

....
tasks.withType<JavaCompile> {
    dependsOn(tasks.openApiGenerate)
}
....

Example Usage

We can then extend the interface and implement our controller methods.

@RestController
public class EmployeeController implements EmployeeApi {
  @Override
  public ResponseEntity<Employee> getEmployees() {
    return ResponseEntity.ok(new Employee().firstName("John").lastName("Lim").age(28));
  }
}

You can find the complete working code here.