Skip to content

Spring Fu is an experimental Kotlin micro-framework based on functional configuration intended to test new ideas for future Spring Boot releases

License

Notifications You must be signed in to change notification settings

nonowak/spring-fu

 
 

Repository files navigation

Build Status

Spring Fu (for functional APIs) is an experimental Kotlin micro-framework based on functional configuration intended to test ideas for upcoming Spring Boot releases. It makes it easy to create lightweight Spring-powered applications with functional APIs instead of annotations. See the 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.

  • Functional bean registration instead of JavaConfig for both internals and application

  • Features are configured explicitly via expressive configuration leveraging Kotlin domain-specific languages (DSL)

  • Flexible programmatic configuration

  • Leverage a subset of Spring Boot

  • Minimal reflection usage

  • No classpath scanning

  • No annotation processing

  • Modules provide configuration DSL and dependencies for other Spring projects and JVM ecosystem

  • Reactive and coroutines web client and server based on WebFlux functional API

  • Reactive and coroutines persistence via Spring Data (MongoDB supported)

  • No blocking web/persistence support is provided since coroutines provides support for imperative programming.

Upcoming features:

In addition to the whole Spring and Reactor teams, credits to Thomas Girard for its spring-webflux-kotlin-dsl experiment that initially demonstrated this approach was possible and to Konrad Kaminski for his awesome spring-kotlin-coroutine project.

Please send us your feedback on the #spring channel of Kotlin Slack. Feel free to open issues, contribute fixes or new modules via pull requests.

Getting started

Spring Fu 0.0.1 is currently under active development so no release is available yet, but you have can try 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 reference documentation.

API documentation will be available shortly.

You can have a look to the sample applications:

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 reified type parameters to avoid type erasure. The beans {} block is in fact a regular ApplicationContextInitializer.

JavaConfig

Functional bean definition

 @Configuration
 class MyConfiguration {

  @Bean
  fun foo() = Foo()

  @Bean
  fun bar(foo: Foo) = Bar(foo)
}
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.

@Component scanning

Functional bean definition

@Component
class Foo {
  // ...
}

@Component
class Bar(private val f: Foo) {
  // ...
}
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.

Annotation-based controller

Kotlin WebFlux routes

@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)
  }
}
routes {
  val r = ref<MyRepository>()
  "/api/article".nest {
    GET("/") {
      r.findAll()
    }
    GET("/{id}") {
      val id = it.pathVariable("id")
      r.findById(id)
    }
  }
}

About

Spring Fu is an experimental Kotlin micro-framework based on functional configuration intended to test new ideas for future Spring Boot releases

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Kotlin 99.9%
  • Other 0.1%