Add support for Kotlin autowired ctors w/ optional params

This commit adds support for autowired constructor parameters
on Kotlin classes with optional parameters. If some constructor
parameters are not available, optional parameter default values
will be used instead. Both explicit @Autowired annotated constructor
and implicit single constructor automatically autowired are supported.

Issue: SPR-15847
This commit is contained in:
Sebastien Deleuze
2017-08-22 16:46:43 +02:00
parent ab6430569d
commit ef68ccdbd8
3 changed files with 225 additions and 13 deletions

View File

@@ -23,51 +23,108 @@ import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.tests.sample.beans.TestBean
import org.junit.Assert.*
import org.springframework.tests.sample.beans.Colour
/**
* Tests for Kotlin support with [Autowired].
*
* @author Juergen Hoeller
* @author Sebastien Deleuze
*/
class KotlinAutowiredTests {
@Test
fun autowiringWithTarget() {
var bf = DefaultListableBeanFactory()
var bpp = AutowiredAnnotationBeanPostProcessor()
fun `Autowiring with target`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
var bd = RootBeanDefinition(KotlinBean::class.java)
val bd = RootBeanDefinition(KotlinBean::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("annotatedBean", bd)
var tb = TestBean()
val tb = TestBean()
bf.registerSingleton("testBean", tb)
var kb = bf.getBean("annotatedBean", KotlinBean::class.java)
val kb = bf.getBean("annotatedBean", KotlinBean::class.java)
assertSame(tb, kb.injectedFromConstructor)
assertSame(tb, kb.injectedFromMethod)
assertSame(tb, kb.injectedField)
}
@Test
fun autowiringWithoutTarget() {
var bf = DefaultListableBeanFactory()
var bpp = AutowiredAnnotationBeanPostProcessor()
fun `Autowiring without target`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
var bd = RootBeanDefinition(KotlinBean::class.java)
val bd = RootBeanDefinition(KotlinBean::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("annotatedBean", bd)
var kb = bf.getBean("annotatedBean", KotlinBean::class.java)
val kb = bf.getBean("annotatedBean", KotlinBean::class.java)
assertNull(kb.injectedFromConstructor)
assertNull(kb.injectedFromMethod)
assertNull(kb.injectedField)
}
@Test // SPR-15847
fun `Autowiring by primary constructor with optional parameter`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
val bd = RootBeanDefinition(KotlinBeanWithOptionalParameter::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("bean", bd)
val tb = TestBean()
bf.registerSingleton("testBean", tb)
val kb = bf.getBean("bean", KotlinBeanWithOptionalParameter::class.java)
assertSame(tb, kb.injectedFromConstructor)
assertEquals("foo", kb.optional)
assertEquals("bar", kb.initializedField)
}
@Test // SPR-15847
fun `Autowiring by annotated primary constructor with optional parameter`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
val bd = RootBeanDefinition(KotlinBeanWithOptionalParameterAndExplicitConstructor::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("bean", bd)
val tb = TestBean()
bf.registerSingleton("testBean", tb)
val kb = bf.getBean("bean", KotlinBeanWithOptionalParameterAndExplicitConstructor::class.java)
assertSame(tb, kb.injectedFromConstructor)
assertEquals("foo", kb.optional)
}
@Test // SPR-15847
fun `Autowiring by annotated secondary constructor with optional parameter`() {
val bf = DefaultListableBeanFactory()
val bpp = AutowiredAnnotationBeanPostProcessor()
bpp.setBeanFactory(bf)
bf.addBeanPostProcessor(bpp)
val bd = RootBeanDefinition(KotlinBeanWithSecondaryConstructor::class.java)
bd.scope = RootBeanDefinition.SCOPE_PROTOTYPE
bf.registerBeanDefinition("bean", bd)
val tb = TestBean()
bf.registerSingleton("testBean", tb)
val colour = Colour.BLUE
bf.registerSingleton("colour", colour)
val kb = bf.getBean("bean", KotlinBeanWithSecondaryConstructor::class.java)
assertSame(tb, kb.injectedFromConstructor)
assertEquals("bar", kb.optional)
assertSame(colour, kb.injectedFromSecondaryConstructor)
}
class KotlinBean(val injectedFromConstructor: TestBean?) {
var injectedFromMethod: TestBean? = null
@Autowired
@@ -79,4 +136,31 @@ class KotlinAutowiredTests {
}
}
class KotlinBeanWithOptionalParameter(
val injectedFromConstructor: TestBean,
val optional: String = "foo"
) {
var initializedField: String? = null
init {
initializedField = "bar"
}
}
class KotlinBeanWithOptionalParameterAndExplicitConstructor @Autowired constructor(
val optional: String = "foo",
val injectedFromConstructor: TestBean
)
class KotlinBeanWithSecondaryConstructor(
val optional: String = "foo",
val injectedFromConstructor: TestBean
) {
@Autowired constructor(injectedFromSecondaryConstructor: Colour, injectedFromConstructor: TestBean, optional: String = "bar") : this(optional, injectedFromConstructor) {
this.injectedFromSecondaryConstructor = injectedFromSecondaryConstructor
}
var injectedFromSecondaryConstructor: Colour? = null
}
}