Skip to content

Kotlin BeanFactory.getBean extension fails with nested generic in context #31439

@ianbrandt

Description

@ianbrandt

With 6.1.0-RC1 (and likely prior), the following test of the BeanFactory.getBean extension fails, presumably due to erasure:

@SpringBootTest
internal class SpringGetBeanNestedGenericIT {

	@Configuration
	class TestConfig {

		@Bean
		fun listOfString(): List<String> =
			listOf("Testing")

		@Bean
		fun listOfListOfString(): List<List<String>> =
			listOf(listOf("Testing"))
	}

	@Test
	fun `test getBean with nested generic`(
		applicationContext: ApplicationContext
	) {
		assertThatNoException().isThrownBy {

			applicationContext.getBean<List<String>>()
		}
	}
}

Error message showing the listOfString and listOfListOfString beans being seen as the same type after erasure:

java.lang.AssertionError: 
Expecting code not to raise a throwable but caught
  "org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'java.util.List' available: expected single matching bean but found 2: listOfString,listOfListOfString
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1310)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:484)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:339)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1192)
	at org.sdkotlin.springdemo.SpringGetBeanNestedGenericIT.test_getBean_with_nested_generic$lambda$0(SpringGetBeanNestedGenericIT.kt:37)
	at org.assertj.core.api.ThrowableAssert.catchThrowable(ThrowableAssert.java:63)
	at org.assertj.core.api.NotThrownAssert.isThrownBy(NotThrownAssert.java:43)
	at org.sdkotlin.springdemo.SpringGetBeanNestedGenericIT.test getBean with nested generic(SpringGetBeanNestedGenericIT.kt:30)

If I remove the listOfListOfString bean from the configuration, the test passes.

I can inject both beans just fine, i.e. the following passes:

@Test
fun `test listOfString is injected`(
	@Autowired
	listOfString: List<String>
) {
	assertThat(listOfString).isEqualTo(LIST_OF_STRING)
}

@Test
fun `test listOfListOfString is injected`(
	@Autowired
	listOfListOfString: List<List<String>>
) {
	assertThat(listOfListOfString).isEqualTo(LIST_OF_LIST_OF_STRING)
}

I notice the BeanFactory.getBeanProvider extension uses the super type token approach. Perhaps this could be used as well for getBean to address this issue:

inline fun <reified T : Any> BeanFactory.getBean(): T =
	getBeanProvider<T>(ResolvableType.forType((object : ParameterizedTypeReference<T>() {}).type)).`object`

If I import that version of the extension instead, the test passes with the listOfListOfString bean in the configuration.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions