$30 off During Our Annual Pro Sale. View Details »

Spring Boot - The Good Parts [DE][Entwicklertag 2020]

Richard
February 20, 2020

Spring Boot - The Good Parts [DE][Entwicklertag 2020]

Entwicklertag 2020: https://entwicklertag.de/frankfurt/2020/spring-boot-%E2%80%93-good-parts-kotlin-level-beginner

Spring ist ein Framework, dass seit 15 Jahren kontinuierlich neue Features bekommt und mit Spring Boot ganz vorne am Zahn der Zeit ist. Die lange Entwicklungszeit führt allerdings auch dazu, dass es für ein Problem weit mehr als eine Lösung im Framework gibt. Viele dieser Features sind den Tradeoff allerdings nicht wert, da sie zwar schnell eingebaut sind aber den Code dann verkomplizieren und die Einstiegshürde für neue Entwickler erhöhen. Dabei kann Spring Boot bereits jetzt explizit, offensichtlich und simpel sein, Beispiele dafür sind nur rar gesät.

In dieser Präsentation schauen wir uns Kotlin Code an der die modernen Features BeanDsl, Functional Web und Spring Fu nutzt. Mit diesen Features können wir erreichen, dass Code wieder innerhalb von Klassen geschrieben wird statt außerhalb in einer eigenwilligen Annotationsprache. Das senkt die Einstiegshürde wiederrum, weil jetzt wieder die Syntax und Kontrollfluss unserer Programmiersprache gilt.

Projekte beginnen oft nicht auf einer grünen Wiese, deswegen betrachten wir auch wie man ein bestehendes Spring Boot Projekt inkrementell auf explizites Verhalten umstellen kann. Am Ende haben wir eine Anwendung deren Kontrollfluss wir in einer Stunde erklären können und nicht mehr raten müssen was zuerst passiert.

Richard

February 20, 2020
Tweet

More Decks by Richard

Other Decks in Programming

Transcript

  1. Spring Boot
    The Good Parts 1 2 3 4
    4 Mein Spring FuncHybrid Beispiel ist: http:/
    /bit.ly/argh-spring-price
    3 Mein Spring Functional Beispiel ist: http:/
    /bit.ly/argh-spring-func
    2 Ein gutes Codebeispiel ist: https:/
    /github.com/sdeleuze/spring-messenger
    1 Inspiriert von "Sébastien Deleuze @ Spring I/O 2019" (Vortrag inkl. Kofu Checking): http:/
    /bit.ly/argh-springio-func
    Richard Gross - pronoun.is/he - User-Need Developer | @arghrich - CC BY 4.0 | speakerdeck.com/richargh | richargh.de

    View Slide

  2. Kotlin Primer nötig?
    2 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  3. Kotlin Primer 1
    !" define class and constructor & fields
    class Foo constructor(val name: String)
    !" functions don't need to be in classes
    fun main(){
    !" types are inferred
    val name = "Trick"
    !" new is not required for instance
    val foo1 = Foo("bar")
    }
    !" functions can have method bodies
    fun doSth(): String {
    return "Track"
    }
    !" functions can be expressions
    fun doSth(): String = "Track"
    3 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  4. Kotlin Primer 2
    !" function parameters can be other functions
    fun display(transform: (Int) !# Int){ val t = transform(42); !$ display t somehow !% }
    !" we can pass functions like this
    fun main(){
    display({ num !# num + 1})
    }
    !" if our function is the !&last!& argument, we don't need ()
    fun main(){
    display { num !# num + 1}
    }
    4 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  5. 1
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    5 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  6. 6 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  7. 7 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  8. Spring Boot
    • Kombiniert alle Spring Projekte (Framework, Data, Security, ...)
    • Spring Projekte meistens Abstraktionen (über ext. Libraries)
    • Garantiert, dass Projekte und ihre ext. Libraries zueinander passen
    • Konfiguriert alle Dependencies "Automagisch" durch Starter und
    @ConditionalOn...
    • Bietet Actuator: Metrics, Health Checks ...
    • Stellt viel Tooling bereit
    8 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  9. Spring Boot
    • Kombiniert alle Spring Projekte
    (Framework, Data, Security, ...)
    • Spring Projekte meistens
    Abstraktionen (über ext. Libraries)
    • Garantiert, dass Projekte und ihre ext.
    Libraries zueinander passen
    • Konfiguriert alle Dependencies
    "Automagisch" durch Starter und
    @ConditionalOn...
    • Bietet Actuator: Metrics, Health
    Checks ...
    9 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  10. Spring+Boot: Bad Parts
    • Nichts geht ohne den Spring Context.
    • Spring Context braucht implizites und langsames
    @ComponentScan + @EnableAutoConfiguration
    • Principle of Most Astonishment -> "Wo kommt die H2 her?"
    • Un-discoverable Programmiersprachen außerhalb von Java/Kotlin
    • interface PersonRepository { findByEmailAndLastname(mail, name) }
    • @ConditionalOn...
    10 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  11. Sprache außerhalb der Sprache
    11

    View Slide

  12. Aber Spring zwingt dich doch
    nicht dazu.
    — Eine Entwicklerin
    12 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  13. Wie denn? Alle Beispiele
    "zwingen" mich doch?
    — Ich vor 2 Jahren
    13 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  14. Spring Boot - Wie man von
    Bad zu Good kommt
    Bad Good
    Langsamer Context Kern für Tests Spring-frei halten
    @ComponentScan Explizite Kotlin beans{}
    Programmiersprachen außerhalb von Java/Kotlin FuncConfig
    @EnableAutoConfiguration Kofu & Least-Astonishment
    14 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  15. 2
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    15 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  16. Spring Docs on Testing
    The POJOs that make up your app should be testable in JUnit or TestNG
    tests, with objects instantiated by using the new operator, without
    Spring or any other container. Emphasizing [these] tests as part of
    your development methodology can boost your productivity.
    — Spring Framework Docs - Testing http:/
    /bit.ly/argh-spocs-unit
    16 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  17. If you can exist without a registry/
    container/framework do so 5
    I (among others) was wrong to shove DI containers down people’s
    throats in 2002 [...]. [We did this because] J2EE apps didn’t have
    primordial entry points where you could compose everything.
    — Paul Hammant 6 7
    7 It turns out we didn't need containers after all http:/
    /bit.ly/richargh-hammant-di
    6 Autor von http:/
    /picocontainer.com/team.html
    5 DI versus BUFA (Paul Hammant) http:/
    /bit.ly/richargh-divbufa
    17 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  18. 18 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  19. Container sind
    hilfreich wenn wir
    1. Beans ganz rechts austauschen müssen
    2. Deklarative Bean-Composition wollen
    3. Lifecycle Management brauchen
    19 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  20. Container machen aber
    auch Tests langsam
    1. Daher Spring an den Rand drängen
    2. Bei Bedarf erzeugt und injected
    Container kleine Factories in Domain 8
    3. Container delegieren Lifecycle-
    Änderungen an Domain
    4. --> Domain-Tests benötigen keine
    Container und sind schnell
    8 Dependency Injection Inversion (Robert Martin) http:/
    /bit.ly/richargh-di-inv
    20 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  21. Exkursion: Lifecycle-Adapter von
    Spring zu Domain bauen
    !" package: domain
    interface Lifecycle {
    fun start()
    fun stop()
    }
    !" package: main
    class SpringLifecycleAdapter(private val components: Set): ApplicationRunner, DisposableBean {
    override fun run(args: ApplicationArguments?) = components.forEach { component !$
    component.start()
    }
    override fun destroy() = components.forEach { component !$
    component.stop()
    }
    }
    21 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  22. Domain sollte frei von Spring
    sein
    22 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  23. 3
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    23 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  24. • Unsere Anwendung -->
    • Was ist das Ziel unseres Anwendungs-
    und Testdesigns?
    24 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  25. Ziel Kontinuierliche
    Sicherheit, dass
    unsere Domain Logik
    noch geht
    25 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  26. 26

    View Slide

  27. Manche Small-Tests
    brauchen Poems
    interface Poems {
    operator fun get(poemId: PoemId): Poem?
    "# add, find, etc.
    }
    27 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  28. Prod & Test benutzen
    daher unterschiedliche
    Repos
    interface Poems {
    fun size(): Int,
    operator fun get(poemId: PoemId): Poem?
    "# add, find, etc.
    }
    "# test
    class InMemoryPoems: Poems {
    private val entities = ConcurrentHashMap()
    override fun get(id: PoemId) = entities.get(id)
    override fun clear() = entities.clear()
    }
    "# prod
    class SpringJpaPoems("% "&): Poems { "% "& }
    28 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  29. Repos synchron halten mit Contracts9
    !" does the interface satisfy the contract? !#
    abstract class PoemsContract(private val poems: Poems){
    @Test
    fun `is empty when no poem has been added`() {
    !$ given: no poems have been added
    !$ when: I ask for the size
    val size = poems.size()
    !$ then: it is empty
    assertThat(size).isEqualTo(0)
    }
    !$ !!%
    }
    !$ checking fake
    @Tags(Tag(contract), Tag(small))
    class InMemoryFakePoemsTest: PoemsContract(InMemoryFakePoems())
    !$ checking prod
    @Tags(Tag(contract), Tag(medium))
    @AllRequiredSpringTestAnnotations
    class SpringJpaPoemsTest(val springJpaPoems: SpringJpaPoems): PoemsContract(springJpaPoems)
    9 Contract Tests by J. B. Rainsberger http:/
    /bit.ly/richargh-contractchecks
    29 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  30. Ziel erreicht
    • Domain bleibt frei von Spring
    • --> Mit Spring Beans definieren wir nun die Interface-
    Implementierung (=Bean)
    30 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  31. 4
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    31 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  32. Kotlin Bean DSL 10
    val beans = beans {
    !" Klasse definieren
    bean()
    !" Bean-Implementierung
    !" und sein !$interface PoetrySlamApi!$ definieren
    bean()
    !" Bean per Supplier definieren
    bean { InMemoryPoems() }
    }
    10 Seit 2016 mit Spring 5 http:/
    /bit.ly/argh-beandsl
    32 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  33. beans {} ist normaler
    Kotlin Code
    !" assign as value
    val webbeans = beans { !#!!$!% }
    !" if/else/loop is allowed
    fun databeans(usePostgres: Boolean): BeanDefinitionDsl {
    return beans {
    if(usePostgres){
    bean()
    }
    }
    }
    !" check environment
    val myBeans = beans {
    if("onlybackend" in env.activeProfiles){
    !" do sth.
    }
    }
    33 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  34. beans {} können
    andere referenzieren
    fun fooConfig() = beans {
    bean()
    bean{
    val jpaPoems = ref()
    PoetrySlam(jpaPoems)
    }
    }
    34 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  35. Bean DSL statt
    @ComponentScan
    @Controller
    class PoetrySlamController(private val poetrySlamApi: PoetrySlamApi){ !" !# }
    interface PoetrySlamApi { !" !# }
    @EnableAutoConfiguration
    class MyApplication
    fun main(args: Array) {
    val beans = beans {
    bean()
    !" !#
    }
    val context = runApplication(*args){
    addInitializers(beans)
    }
    }
    35 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  36. Ziel erreicht
    • Kotlin Beans DSL ist normaler, expliziter, schneller Kotlin-Code
    • --> Wir können auch andere Spring Projekts funktional&explizit
    konfigurieren
    36 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  37. 5
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    37 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  38. SpringFramework.web
    RouterFunctionDsl
    class PoetrySlamHandler {
    fun index(serverRequest: ServerRequest) = ServerResponse.ok().body("Hi")
    fun authors(serverRequest: ServerRequest) = ServerResponse.ok()
    .body(listOf("Macy", "Diddy", "Kevin"))
    }
    fun poetrySlamRouter(poetrySlamHandler: PoetrySlamHandler) = router {
    accept(APPLICATION_JSON).nest {
    GET("/", poetrySlamHandler!"index)
    "/authors".nest {
    GET("/", poetrySlamHandler!"authors)
    }
    }
    }
    38 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  39. SpringFramework.data.r2dbc
    DatabaseClient
    fun main() {
    val client = createClient()
    !" functional db query
    val affectedRows = client
    .execute("SELECT * from Poems")
    .fetch().rowsUpdated()
    }
    private fun createClient(): DatabaseClient{
    val h2config = H2ConnectionConfiguration.builder().url("mem:testdb").build()
    val connectionFactory = H2ConnectionFactory(h2config)
    return DatabaseClient.create(connectionFactory)
    }
    39 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  40. Ziel erreicht
    • Spring stellt explizite und schnelle Funktionen zum Konfigurieren
    von Web & Data bereit
    • --> In Zukunft können wir auch die Spring Projekts explizit
    konfigurieren
    40 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  41. 6
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Migrate | Recap
    41 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  42. Kofu: Kotlin Functional 11
    Kofu is a minimalist, efficient and explicit configuration model for
    Spring Boot, using a Kotlin DSL [...] with great discoverability thanks to
    auto-complete [...] with fast startup. [...] It is not intended to be used in
    production yet. 12
    12 Spring Incubator: https:/
    /github.com/spring-projects-experimental/spring-fu/tree/master/kofu
    11 Basiert auf Spring Functional https:/
    /github.com/sdeleuze/spring-kotlin-functional
    42 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  43. Baut auf bestehendem Spring auf
    • beans{} DSL vom Spring Framework
    • router{} DSL vom Spring MVC bzw. Spring WebFlux
    • Die neue application{} und configuration{} DSL ist ein
    Adapter für bestehende *AutoConfiguration Klassen
    43 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  44. Kofu Beispiel: Minimal Servlet
    val myApp = application(WebApplicationType.SERVLET) {
    webMvc {
    port = 8080
    }
    !" r2dbcH2 { !!# }
    !" logging { !!# }
    !" listener { !!# }
    }
    fun main(args: Array) {
    myApp.run(args)
    }
    !" optional: place an index.html in src/main/resources to avoid 404
    44 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  45. Kofu DSL ist noch nicht fertig
    • v0.1 (05.2019), v0.2 (10.2019), v0.3 (01.2020)
    • DSL ist Adapter für bestehende *AutoConfiguration
    • Mit DSL sind noch nicht alle Projekte konfigurierbar
    • Mit DSL ist noch nicht alles vom Projekt konfigurierbar
    • DSL könnte sich noch ändern bis v1.0 (x.2020?)
    45 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  46. 7
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Hybrid | Recap
    46 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  47. Functional Beans mit alten Beans
    mischen
    @Configuration
    open class MyOldConfiguration { "# "$ }
    fun createProdBeans() = beans { "# "$ }
    @Import(MyOldConfiguration"%class)
    @EnableAutoConfiguration
    class MyApplication
    fun main(args: Array) {
    runApplication(*args){
    addInitializers(createProdBeans())
    }
    }
    47 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  48. Beans in Tests benutzen
    class FunctionalConfigAdapter: ApplicationContextInitializer {
    override fun initialize(context: GenericApplicationContext) {
    val beans = createProdBeans()
    beans.initialize(context)
    }
    }
    @ExtendWith(SpringExtension"#class)
    @ContextConfiguration(
    initializers = [AFunctionalComponentConfig"#class],
    classes = [AnOldComponentConfig"#class])
    class ATest {
    @Test
    fun `test something`(@Autowired poetrySlam: PoetrySlam){
    "$ ""%
    }
    }
    48 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  49. 8
    Spring Vs Boot | Container | Design4Testability | SpringBeans | FuncConfig | Kofu | Hybrid | Recap
    49 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  50. Good to Bad
    Bad Good
    Langsamer Context Kern für Tests Spring-frei halten
    @ComponentScan Explizite Kotlin beans{}
    Programmiersprachen außerhalb von Java/Kotlin FuncConfig
    @EnableAutoConfiguration Bald Kofu & Least-Astonishment
    50 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  51. The Good Parts
    Eine Spring Boot Anwendung geht auch:
    • in Millisekunden testbar mit Springless Domain
    • schnell
    • explizit
    • schrittweise zu migrieren
    51 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  52. The Other Good Parts
    • Oliver Drothbom: Hypermedia=REST, Connascence
    • Sebastien Deleuze: Functional Spring
    • Josh Long: Spring Blogging und Beispiele
    • uvm.
    52 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  53. Danke
    • Feedback --->
    • Mehr zu Kofu und explicit Spring 1 2
    • Noch mehr explicit Spring Beispiele 3 4
    • Mehr Beans 13 14
    • Mehr funktionale Routen 15
    15 https:/
    /spring.io/blog/2016/09/22/new-in-spring-5-functional-web-framework
    14 Mehr Beans https:/
    /github.com/sdeleuze/spring-kotlin-functional/blob/master/src/main/
    kotlin/functional/Beans.kt
    13 Spring 5 Functional: https:/
    /spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-
    functional-way
    4 Mein Spring FuncHybrid Beispiel ist: http:/
    /bit.ly/argh-spring-price
    3 Mein Spring Functional Beispiel ist: http:/
    /bit.ly/argh-spring-func
    2 Ein gutes Codebeispiel ist: https:/
    /github.com/sdeleuze/spring-messenger
    1 Inspiriert von "Sébastien Deleuze @ Spring I/O 2019" (Vortrag inkl. Kofu Checking): http:/
    /bit.ly/
    argh-springio-func
    53 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  54. Backup
    54 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  55. Extension für Reset/Clear schreiben
    !" extension to reset everything beforeEach
    class ClearResetJunitExtension: org.junit.jupiter.api.extension.BeforeEachCallback {
    override fun beforeEach(extCon: ExtensionContext) {
    SpringExtension.getApplicationContext(extCon).getBeansOfType(Clearable!#class.java).forEach {
    it.value.clear()
    }
    SpringExtension.getApplicationContext(extCon).getBeansOfType(Resettable!#class.java).forEach {
    it.value.reset()
    }
    }
    }
    !" example usage
    @Tags(Tag(medium))
    @ExtendWith(ClearResetJunitExtension!#class, SpringExtension!#class)
    @ContextConfiguration(initializers = [AFunctionalComponentConfig!#class])
    @TestPropertySource(locations = ["classpath:application.properties"]) !" Optional
    class ATest {
    @Test
    fun `test something`(@Autowired aFactory: AFactory){
    val bar = aFactory.bar()
    !" etc.
    }
    }
    55 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  56. Kofu Beispiel: Properties
    val myApp = application(WebApplicationType.SERVLET) {
    configurationProperties(prefix = "poetry-slam")
    webMvc {
    port = 8080
    }
    }
    data class PoetrySlamProperties(val name: String)
    fun main(args: Array) {
    val context = myApp.run(args)
    val properties = context.getBean()
    println("The slam is called [${properties.name}]")
    }
    #$ src/main/resources/application.properties contains
    poetry-slam.name=The Hits 1789
    56 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  57. Kofu Beispiel: Trennen von Web, Data
    und Domain
    val webConfig = configuration {
    beans { bean(); bean("#poetrySlamRouter) }
    webMvc {
    port = 8080
    converters { string(); jackson() }
    }
    listener { println("Application Ready") }
    }
    val domainConfig = configuration { "$ ""% "& }
    val dataConfig = configuration { "$ ""% "& }
    val myApp = application(WebApplicationType.SERVLET) {
    enable(domainConfig)
    enable(webConfig)
    if(theAnswer "' 42) "( programmatically disable
    enable(dataConfig)
    }
    fun main(args: Array) {
    myApp.run(args)
    }
    57 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  58. Kofu Application Start
    !" beans erben von ApplicationContextInitializer
    internal val kapaCoreConfigs = beans {
    addIntegratingComponentsConfigs()
    }
    !" dependencies auf andere Komponenten bewusst rausstellen
    internal val kapaCoreDependencyConfigs = beans {
    addSharedKernelConfigs()
    if(env.isMonolith()){
    addLokationseingangConfigs()
    }
    }
    @SpringBootConfiguration
    @EnableAutoConfiguration
    open class Application
    fun main(args: Array) = runApplication(*args){
    addInitializers(kapaCoreDependencyConfigs)
    addInitializers(kapaCoreConfigs)
    }
    58 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide

  59. Spring kann mehr als
    Annotationen
    • 2004 Spring 1.0 mit Xml Beans
    • 2009 Spring 3.0 mit @Configuration und @Bean
    reference/
    • 2013 Spring 4.0 mit Groovy beans {}
    • 2016 Spring 5.0 mit Kotlin beans {}
    • Die Bean DSL ist nicht experimentell
    59 @arghrich - CC BY 4.0 | speakerdeck.com/richargh

    View Slide