spring-fu

Introduction: Spring Fu is an experimental Kotlin micro-framework that makes it easy to create lightweight Spring-powered applications with functional APIs instead of annotations
More: Author   ReportBugs   
Tags:

image::https://ci.spring.io/api/v1/teams/spring-fu/pipelines/spring-fu/badge["Build Status", link="https://ci.spring.io/teams/spring-fu/pipelines/spring-fu"]

Spring Fu (for functional APIs) is an experimental https://kotlinlang.org/[Kotlin] micro-framework based on functional configuration intended to test ideas for upcoming https://spring.io/projects/spring-boot[Spring Boot] releases. It makes it easy to create lightweight https://spring.io/projects/spring-framework[Spring]-powered applications with <> instead of annotations. See the https://repo.spring.io/snapshot/org/springframework/fu/spring-fu-reference/0.0.1.BUILD-SNAPSHOT/spring-fu-reference-0.0.1.BUILD-SNAPSHOT.html[reference documentation] for more details.

== Overview

A simple Spring Fu web application rendering an HTML page and exposing a JSON HTTP endpoint looks like that:

fun main(args: Array<String>) = application {
    bean<UserRepository>()
    bean<UserHandler>()
    webflux {
        val port = if (profiles.contains("test")) 8181 else 8080
        server(netty(port)) {
            mustache()
            codecs {
                jackson()
            }
            routes(ref = ::routes)
        }
    }
}.run(await = true)

fun routes() = routes {
    val userHandler = ref<UserHandler>()
    GET("/", userHandler::listView)
    GET("/api/user", userHandler::listApi)
}

It leverages a subset of Spring Boot, Spring Framework, Spring Data and Spring Security to provide an alternative configuration model for developing applications in Kotlin.

Upcoming features:

In addition to the whole Spring and Reactor teams, credits to https://github.com/tgirard12[Thomas Girard] for its https://github.com/tgirard12/spring-webflux-kotlin-dsl[spring-webflux-kotlin-dsl] experiment that initially demonstrated this approach was possible and to https://github.com/konrad-kaminski[Konrad Kaminski] for his awesome https://github.com/konrad-kaminski/spring-kotlin-coroutine[spring-kotlin-coroutine] project.

Please send us your feedback on the #spring channel of http://slack.kotlinlang.org/[Kotlin Slack]. Feel free to open issues, https://raw.githubusercontent.com/spring-projects/spring-fu/master/CONTRIBUTING.adoc[contribute] fixes or new modules via pull requests.

== Getting started

Spring Fu https://github.com/spring-projects/spring-fu/milestone/1[`0.0.1`] is currently under active development so no release is available yet, but you have can try https://repo.spring.io/snapshot/org/springframework/fu/[`0.0.1.BUILD-SNAPSHOT` builds].

=== Bootstrap

A Spring Fu bootstrap is an archive containing minimal project templates designed to allow you getting started quickly and easily Spring Fu applications. To start a new project, download a bootstrap .zip archive, extract it and follow README.adoc instructions.

=== Documentation

To start with Spring Fu, you can read the https://repo.spring.io/snapshot/org/springframework/fu/spring-fu-reference/0.0.1.BUILD-SNAPSHOT/spring-fu-reference-0.0.1.BUILD-SNAPSHOT.html[**reference documentation**].

API documentation will be available https://github.com/spring-projects/spring-fu/issues/8[shortly].

You can have a look to the sample applications:

[[functional-configuration]] == Functional configuration

Spring Fu functional configuration is leveraging Kotlin DSL that allows you to configure your application explicitly. Each custom block like configuration, actuators or webflux is in fact a more high level beans {} block with a custom DSL provided for easy configuration. Since this configuration is code, you can use any kind of custom programmatic bean registration without having to implement your own @Conditional annotation.

Here is an example of a typical Spring Fu application functional configuration.

fun main(args: Array<String) = application {
    configuration {
        AppConfiguration(name = env["SYSTEM_ENV"] ?: "default")
    }
    actuators(beans = false, mapping = false)
    logging {
        level(INFO)
        level("org.springframework", DEBUG)
        logback {
            consoleAppender()
            rollingFileAppender(file = File("log.txt"))
        }
    }
    profile("data") {
        bean<UserRepository>()
        bean<ArticleRepository>()
        mongodb(uri = "mongodb://myserver.com/foo")
        listener<ContextStartedEvent> {
            ref<UserRepository>().init()
            ref<ArticleRepository>().init()
        }
    }
    profile("web") {
        bean<HtmlHandler>()
        bean<ApiHandler>()
        webflux {
            val port = if (profiles.contains("test")) 8181 else 8080
            server(netty(port)) {
                cors(origin = "example.com")
                mustache()
                codecs {
                    jackson()
                    protobuf()
                }
                routes(ref = ::routes)
                security { // TODO }
            }
            client {
                codecs {
                    jackson()
                }
            }
        }
    }
}.app.run(await = true, profiles = "data, web")

data class AppConfiguration(
    val name: String,
    val remoteUrl: String  = "http://localhost:8080"
)

fun routes() = routes {
    val htmlHandler = ref<HtmlHandler>()
    val apiHandler = ref<ApiHandler>()
    GET("/", htmlHandler::blog)
    GET("/article/{id}", htmlHandler::article)
    "/api".nest {
        GET("/", apiHandler::list)
        POST("/", apiHandler::create)
        PUT("/{id}", apiHandler::update)
        DELETE("/{id}", apiHandler::delete)
    }
}

=== Comparison with JavaConfig

Functional bean definition allows to define beans in an efficient way with minimal reflection usage, no proxy and with a concise Kotlin DSL that takes advantage of https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters[reified type parameters] to avoid type erasure. The beans {} block is in fact a regular https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContextInitializer.html[`ApplicationContextInitializer`].

|===== a|JavaConfig |Functional bean definition a|

 @Configuration
 class MyConfiguration {

  @Bean
  fun foo() = Foo()

  @Bean
  fun bar(foo: Foo) = Bar(foo)
}

a|

val myConfiguration = beans {
  bean<Foo>()
  // Implicit autowiring by constructor
  bean<Bar>()
}
|=====

=== Comparison with `@Component`

Functional bean definition is explicit, does not imply any classpath scanning and supports constructor parameters autowiring.

|=====
a|**`@Component` scanning** |**Functional bean definition**
a|
```kotlin
@Component
class Foo {
  // ...
}

@Component
class Bar(private val f: Foo) {
  // ...
}

a|

class Foo {
  // ...
}
class Bar(private val f: Foo) {
  // ...
}

beans {
  bean<Foo>()
  bean<Bar>()
}
|=====

=== Comparison with controllers

Kotlin WebFlux router provides a simple but powerful way to implement your web application. HTTP API, streaming but also view rendering are supported.

|=====
a|**Annotation-based controller** |**Kotlin WebFlux routes**
a|
```kotlin
@RestController
@RequestMapping("/api/article")
class MyController(private val r: MyRepository) {

  @GetMapping("/")
  fun findAll() =
    r.findAll()

  @GetMapping("/{id}")
  fun findOne(@PathVariable id: Long) =
    repository.findById(id)
  }
}

a| ```kotlin routes { val r = ref() "/api/article".nest { GET("/") { r.findAll() } GET("/{id}") { val id = it.pathVariable("id") r.findById(id) } } } |=====

Support Me
Apps
About Me
Google+: Trinea trinea
GitHub: Trinea