close

Spring Fu 0.3.0 and beyond

I am happy to announce that Spring Fu 0.3.0 is available. As a reminder, Spring Fu is an incubator for Spring Boot programmatic configuration using DSLs in order to configure it explicitly with code in a declarative way, achieving great discoverability thanks to auto-complete.

JaFu is back!

This new milestone brings back JaFu (the Java DSL) in addition to KoFu (the Kotlin DSL). JaFu was removed in 0.1.0 because at that point, I had not the bandwidth to support both Java and Kotlin DSLs, and in term of API I was not sure that the Java variant was attractive enough to justify such effort, but I have changed my mind for various reasons:

  • I received a lot of requests to bring it back

  • Java as a language is moving faster

  • Getting more control on how configuration is applied is interesting for Java developers as well as Kotlin ones

  • Functional approach is naturally very efficient on the JVM and a good fit with GraalVM native images

  • 2 great new contributors have join the team: Audrey Neveu with a focus on KoFu and Arjen Poutsma with a focus on JaFu. Welcome to them!

Other improvements

This new release is also the opportunity for various improvements like a Spring Boot 2.3.0 baseline or API improvements, see the detailed changelog here.

Spring Fu 0.3.0 also ships with additional optimizations which enable only the functional Web infrastructure, so on my laptop I get pretty fast startup time on OpenJDK 11: Started Application in 0.673 seconds (JVM running for 0.898).

Comparing JaFu and Kofu

Since Spring MVC is now usable in a functional way for both Java and Kotlin, let’s see what looks like a minimal Spring Boot web application configured with those DSLs.

With JaFu:

public class Application {

	public static JafuApplication app = webApplication(a -> a.beans(b -> b
		.bean(SampleHandler.class)
		.bean(SampleService.class))
		.enable(webMvc(w -> w
			.port(w.profiles().contains("test") ? 8181 : 8080)
			.router(router -> {
				SampleHandler handler = w.ref(SampleHandler.class);
				router
					.GET("/", handler::hello)
					.GET("/api", handler::json);
			}).converters(c -> c
				.string()
				.jackson(j -> j.indentOutput(true))))));

	public static void main (String[] args) {
		app.run(args);
	}
}

With KoFu:

val app = webApplication {
	beans {
		bean<SampleService>()
		bean<SampleHandler>()
	}
	webMvc {
		port = if (profiles.contains("test")) 8181 else 8080
		router {
			val handler = ref<SampleHandler>()
			GET("/", handler::hello)
			GET("/api", handler::json)
		}
		converters {
			string()
			jackson {
				indentOutput = false
			}
		}
	}
}

fun main() {
	app.run()
}

Configuration slices

The power of this configuration model lies in the fact that you can define your own configuration slices and assemble them as you want. For example, let’s define 3 configuration slices webConfig, loggingConfig and myFeatureConfig:

val webConfig = configuration {
    webMvc {
        // ...
    }
}

val loggingConfig = configuration {
    logging {
        level = LogLevel.WARN
    }
}

val myFeatureConfig = configuration {
    beans {
        // ...
    }
    cassandra {
        // ...
    }
}

You can then use them all for your regular web application:

val webApp = webApplication {
    enable(loggingConfig)
    enable(myFeatureConfig)
    enable(webConfig)
}

fun main() {
    webApp.run()
}

But you can also use just a subset for your integration tests for example:

@Test
fun `My feature integration test`() {
    val testApp = application {
        enable(loggingConfig)
        enable(myFeatureConfig)
    }
    // ...
}

Roadmap

In term of roadmap, the next 0.4.0 milestone will focus on:

As usual, feedback is welcome. Be aware that we have for now mainly a focus on getting the right software design and APIs rather than an extensive coverage of more features.

comments powered by Disqus