Functional Spring Boot

About Me

John Burns

Sr. Staff Platform Engineer @ GrubHub

CJUG/CKUG Co-Organizer

ktlint-gradle & spring-funk Maintainer

github logo wakingrufus

fediverse logo @wakingrufus@bigshoulders.city

GrubHub logo

In the Beginning...

XML

Spring XML

Spring Boot


@SpringBootApplication
@EnableWebMvc
public class MyApplication {
}
@Service
public class MyBusinessLogic {
    public String doSomething();
}
@RestController
public class MyController {
    private final MyBusinessLogic service;
    @Autowired
    public MyController(MyBusinessLogic service) {
        this.service = service;
    }
    @GetMapping(path = "/ping")
    public String get() {
        return service.doSomething();
    }
}
                

Spring Boot: The Good

  • No XML
  • Wiring lives closer to code
  • Less boilerplate

Spring Boot: The Bad

  • Learning Curve
  • Performance
  • Structureless

Eliminate Component Scan


@SpringBootConfiguration
@EnableAutoConfiguration
@EnableWebMvc
public class MyApplication {
    @Bean
    MyBusinessLogic myBusinessLogic() {
        return MyBusinessLogic();
    }
    @Bean
    MyController myController(MyBusinessLogic myBusinessLogic) {
        return MyController(myBusinessLogic);
    }
}
public class MyBusinessLogic {
    public String doSomething();
}
@RestController
public class MyController {
    private final MyBusinessLogic service;
    public MyController(MyBusinessLogic service) {
        this.service = service;
    }
    @GetMapping(path = "/ping")
    public String get() {
        return service.doSomething();
    }
}
                

Remaining Issues

  • Still need AutoConfiguration
  • Still using Annotations
  • Boilerplate

Programmatic Configuration

  • Slightly more boilerplate
  • Much easier to reason about
  • Tests are very fast
  • App Startup is very fast

Spring Fu

  • Converts all Autoconfigs to Initializers
  • Provides DSLs
  • All-in
  • Spring pivoted to hints for graalvm

Beans Dsl


val beans = beans {
    bean<MyBusinessLogic>()
    bean<MyBusinessLogicWithInjection> {
        MyBusinessLogicWithInjection(ref())
    }
    bean<MyBusinessLogicWithInjectionByName>("beanName") {
        MyBusinessLogicWithInjectionByName(ref("name"))
    }
}

Router Dsl


class MyBusinessLogic {
    fun doSomething(request: ServerRequest): ServerResponse
}
val myRoutes(service: MyBusinessLogic) = router {
    GET("/ping", service::doSomething)
    POST("/ping", service::doSomethingElse)
}

Functional Spring


val myRoutes(service: MyBusinessLogic) = router {
    GET("/ping", service::doSomething)
}
val beans = beans {
    bean<MyBusinessLogic>()
    bean(::myRoutes)
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableWebMvc
open class MyApplication
  : ApplicationContextInitializer<GenericApplicationContext> {
    override fun initialize(context: GenericApplicationContext) {
        beans.initialize(context)
    }
}
class MyBusinessLogic {
    public String doSomething();
}

Functional Spring 7.x


val myRoutes(service: MyBusinessLogic) = router {
    GET("/ping", service::doSomething)
}
@SpringBootConfiguration
@EnableAutoConfiguration
@EnableWebMvc
@Import(SampleBeanRegistrar::class)
open class MyApplication {
}
class MyBusinessLogic {
    public String doSomething();
}
class SampleBeanRegistrar : BeanRegistrarDsl({
    registerBean<MyBusinessLogic>()
    registerBean(::myRoutes)
})

Remaining Issues

  • Still need AutoConfiguration
  • Not extensible

Spring Funk


open class MyApplication : SpringFunkApplication {
    override fun dsl(): SpringDslContainer.() -> Unit = {
        beans {
            bean<MyBusinessLogic>()
        }
        webmvc {
            enableWebMvc {
                jetty()
            }
            routes {
                router { service: MyBusinessLogic -> {
                        GET("/ping", service::doSomething)
                    }
                }
            }
        }
    }
}
class MyBusinessLogic {
    public String doSomething();
}

Demo

Resources

Spring Docs

Spring Fu

Spring Funk

Sébastien Deleuze Demo

github logo wakingrufus

fediverse logo @wakingrufus@bigshoulders.city