Skip to content

Latest commit

 

History

History
181 lines (147 loc) · 5.52 KB

exercise-4.md

File metadata and controls

181 lines (147 loc) · 5.52 KB

Exercise 4: Spring 5 Kotlin bean definition DSL

In this exercise we will show a real world example of how to use a DSL written in Kotlin. We will use the Spring 5 Kotlin bean definition DSL as we rewrite our bean configuration in the BootiqueApplication class to use this DSL.

Configuring Spring beans with the Kotlin bean definition DSL

Spring 5 provides a Kotlin bean definition DSL to define your configuration in a different way. An example from the Spring Documentation:

fun dsl() = beans {
    bean<UserHandler>()
    bean<Routes>()
    bean<WebHandler>("webHandler") {
        RouterFunctions.toWebHandler(
            ref<Routes>().router(),
            HandlerStrategies.builder().viewResolver(ref()).build()
        )
    }
    bean("messageSource") {
        ReloadableResourceBundleMessageSource().apply {
            setBasename("messages")
            setDefaultEncoding("UTF-8")
        }
    }
    bean {
        val prefix = "classpath:/templates/"
        val suffix = ".mustache"
        val loader = MustacheResourceTemplateLoader(prefix, suffix)
        MustacheViewResolver(Mustache.compiler().withLoader(loader)).apply {
            setPrefix(prefix)
            setSuffix(suffix)
        }
    }
    profile("foo") {
        bean<Foo>()
    }
}

Lets migrate the bean definition in the BootiqueApplication to the Kotlin bean definition DSL. We can do that by adding a Kotlin function that returns an instance of a BeanDefinitionDsl.

fun dsl(): BeanDefinitionDsl = beans {
    bean<T> { 
        ...instantiation
    }
}

Exercise: add the dsl() function in the BootiqueApplication class.

Now we included the dsl() function we can migrate the existing bean definition.

Exercise: add the @Bean fun api(): Docket to the bean DSL and remove the @Bean fun api(): Docket function.

You are now done with the conversion to the DSL.

Suggested solution:
fun dsl() = beans {
    bean<Docket> {
        Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.bootique.bootique"))
                .build()
    }
}

Spring Boot 2 and Kotlin

Spring Boot 2 provides a few convenient Kotlin extensions. There is an extension function available which allows you to easily use the Kotlin bean definition DSL in the SpringApplication runner, see the snippet below:

companion object {
    @JvmStatic
    fun main(args: Array<String>) {
        runApplication<BootiqueApplication>(*args) {
            addInitializers(...add BeanDefinitionDsl instances here...)
        }
    }
}

Exercise: Replace the existing main implementation by the runApplication extension and include the BeanDefinitionDsl in the addInitializers(...). Does it compile?

Suggested solution:
/**
 * Spring boot application with Swagger2 enabled.
 */
@SpringBootApplication
@EnableSwagger2
class BootiqueApplication {

    /**
     * Swagger2 configuration.
     */
    fun dsl() = beans {
        bean<Docket> {
            Docket(DocumentationType.SWAGGER_2)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.bootique.bootique"))
                    .build()
        }
    }

    companion object {
        /**
         * Runs the Spring boot application.
         */
         @JvmStatic
        fun main(args: Array<String>) {
            runApplication<BootiqueApplication>(*args) {
                addInitializers(dsl()) // Wont compile
            }
        }
    }
}

This was a trick question, because this wont compile. Since main is marked as @JvmStatic we cannot access the dsl() function from a static context. In Kotlin we dont need a class/companion object to run the application, we can move the main function to a top-level function. Optionally we can also inline the dsl() function inside runApplication.

Exercise: Change the main to a top-level function.

We can define the fun dsl() as a top-level function, but we can also inline the body into the runApplication code.

Exercise: Inline the body of fun dsl() inside of the runApplication { ... } block

Suggested solution:
/**
 * Spring boot application with Swagger2 enabled.
 */
@SpringBootApplication
@EnableSwagger2
class BootiqueApplication

fun main(args: Array<String>) {
    runApplication<BootiqueApplication>(*args) {
        addInitializers(
            beans {
                bean<Docket> {
                    Docket(DocumentationType.SWAGGER_2)
                        .select()
                        .apis(RequestHandlerSelectors.basePackage("com.bootique.bootique"))
                        .build()
                }
            }
        )
    }
}

Exercise: Build and run the project using maven and see if everything is still working as expected.

We have now successfully migrate the old configuration to the Kotlin bean definition DSL.

Next steps

Your are almost there!

Continue with exercise-5:

You can either start fresh by switching to the exercise-5 branch or continue on your current branch.

Switching to the exercise-5 branch can be done using IntelliJ or in your terminal by issuing the following command:

git checkout exercise-5