Add 'fromApplication' and 'with' Kotlin extension functions

Update `SpringApplicationExtensions.kt` with `fromApplication` and
`with` functions that make `SpringApplication.from(...)` easier to use
with Kotlin.

Fixes gh-35756
This commit is contained in:
Phillip Webb 2023-06-05 21:18:58 -07:00
parent ff35ed4be1
commit 1669b81af7
5 changed files with 69 additions and 15 deletions

View File

@ -16,12 +16,8 @@
package org.springframework.boot.docs.features.testing.testcontainers.atdevelopmenttime.launch
import org.springframework.boot.SpringApplication
import org.springframework.boot.docs.features.testing.testcontainers.atdevelopmenttime.launch.main as myApplicationMain
import org.springframework.boot.fromApplication
object Main {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.from(::myApplicationMain).run(*args)
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().run(*args)
}

View File

@ -16,12 +16,9 @@
package org.springframework.boot.docs.features.testing.testcontainers.atdevelopmenttime.test
import org.springframework.boot.SpringApplication
import org.springframework.boot.docs.features.testing.testcontainers.atdevelopmenttime.test.main as myApplicationMain
import org.springframework.boot.fromApplication
import org.springframework.boot.with
object Main {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.from(::myApplicationMain).with(MyContainersConfiguration::class.java).run(*args)
}
}
fun main(args: Array<String>) {
fromApplication<MyApplication>().with(MyContainersConfiguration::class).run(*args)
}

View File

@ -17,6 +17,11 @@
package org.springframework.boot
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.util.Assert
import org.springframework.util.ClassUtils
import org.springframework.util.ReflectionUtils
import kotlin.reflect.KClass
import kotlin.reflect.KType
/**
* Top-level function acting as a Kotlin shortcut allowing to write
@ -40,3 +45,34 @@ inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableAp
*/
inline fun <reified T : Any> runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext =
SpringApplication(T::class.java).apply(init).run(*args)
/**
* Top-level function acting as a Kotlin shortcut allowing to write
* `fromApplication<MyApplication>().with(...)`. This method assumes that
* the `main` function is declared in the same file as `T`.
*
* @author Phillip Webb
* @since 3.1.1
*/
inline fun <reified T : Any> fromApplication(): SpringApplication.Augmented {
val type = T::class
val ktClassName = type.qualifiedName + "Kt"
try {
val ktClass = ClassUtils.resolveClassName(ktClassName, type.java.classLoader)
val mainMethod = ReflectionUtils.findMethod(ktClass, "main", Array<String>::class.java)
Assert.notNull(mainMethod, "Unable to find main method")
return SpringApplication.from { ReflectionUtils.invokeMethod(mainMethod!!, null, it) }
} catch (ex: Exception) {
throw IllegalStateException("Unable to use 'fromApplication' with " + type.qualifiedName)
}
}
/**
* Extension function that allows `SpringApplication.Augmented.with` to work with Kotlin classes.
*
* @author Phillip Webb
* @since 3.1.1
*/
fun SpringApplication.Augmented.with(type: KClass<*>): SpringApplication.Augmented {
return this.with(type.java)!!
}

View File

@ -16,9 +16,11 @@
package org.springframework.boot
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatIllegalStateException
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.getBean
import org.springframework.boot.kotlinsample.TestKotlinApplication
import org.springframework.boot.web.servlet.server.MockServletWebServerFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@ -67,6 +69,18 @@ class SpringApplicationExtensionsTests {
assertThat(environment).isEqualTo(context.environment)
}
@Test
fun `Kotlin fromApplication() top level function`() {
val context = fromApplication<TestKotlinApplication>().with(ExampleWebConfig::class).run().applicationContext
assertThat(context.getBean<MockServletWebServerFactory>()).isNotNull
}
@Test
fun `Kotlin fromApplication() top level function when no main`() {
assertThatIllegalStateException().isThrownBy { fromApplication<ExampleWebConfig>().run() }
.withMessage("Unable to use 'fromApplication' with org.springframework.boot.SpringApplicationExtensionsTests.ExampleWebConfig")
}
@Configuration(proxyBeanMethods = false)
internal open class ExampleWebConfig {

View File

@ -0,0 +1,11 @@
package org.springframework.boot.kotlinsample
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
open class TestKotlinApplication
fun main(args: Array<String>) {
runApplication<TestKotlinApplication>(*args)
}