Kotlin实现SpringContext获取Bean报NoUniqueBeanDefinitionException解决方案
问题背景
我们可以通过实现了ApplicationContextAware接口的SpringContext类提供的getBean方法,在非Spring托管类中获取Spring容器管理的Bean,原Java实现代码如下:
@Component public class SpringContext implements ApplicationContextAware { private static ApplicationContext context; /** * Returns the Spring managed bean instance of the given class type if it exists. * Returns null otherwise. * @param beanClass * @return */ public static <T extends Object> T getBean(Class<T> beanClass) { return context.getBean(beanClass); } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { // store ApplicationContext reference to access required beans later on SpringContext.context = context; } }
尝试转换为Kotlin版本后代码如下:
@Component class SpringContext : ApplicationContextAware { @Throws(BeansException::class) override fun setApplicationContext(context: ApplicationContext) { // store ApplicationContext reference to access required beans later on SpringContext.Companion.context = context } companion object { private var context: ApplicationContext? = null fun <T : Any>getBean(beanClass: KClass<out T>): Any { return context!!.getBean(beanClass) } } }
调用时抛出如下异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available: more than one 'primary' bean found among candidates: [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.annotation.internalPersistenceAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory....] at org.springframework.beans.factory.support.DefaultListableBeanFactory.determinePrimaryCandidate(DefaultListableBeanFactory.java:1591) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1188) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:420) at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:350) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1133)
经排查发现调用时传入的beanClass参数被识别为java.lang.Object类型。
问题产生原因
核心问题出在Kotlin版本getBean方法的返回值声明上:
- 方法定义的泛型参数
T仅用于入参KClass<out T>的约束,没有和返回值绑定,返回值被显式声明为Any类型 - 当调用该方法时,编译器无法根据返回值目标类型推导泛型参数
T的实际类型,会默认将T向上推导为Any,对应传入Spring容器查询的类型就是java.lang.Object - Spring容器内所有Bean都是
Object的子类,自然会匹配到大量候选Bean,最终抛出无唯一Bean的异常
解决方案
修正getBean方法的返回值声明,将返回类型绑定到泛型参数T即可。如果需要适配Kotlin的使用习惯,还可以额外增加带reified泛型的重载方法,调用时不需要手动传入KClass参数。
修正后的完整Kotlin代码如下:
@Component class SpringContext : ApplicationContextAware { @Throws(BeansException::class) override fun setApplicationContext(context: ApplicationContext) { // 存储ApplicationContext引用,后续用于获取Bean SpringContext.context = context } companion object { private var context: ApplicationContext? = null // 修正返回值为泛型T,而非Any fun <T : Any> getBean(beanClass: KClass<out T>): T { return context!!.getBean(beanClass) } // 可选:新增reified泛型重载,调用更便捷 inline fun <reified T : Any> getBean(): T { return context!!.getBean(T::class) } } }
修正后的调用示例:
- 兼容原有传参方式:
val userService = SpringContext.getBean(UserService::class) - Kotlin专属简化调用:
val userService = SpringContext.getBean<UserService>()
内容的提问来源于stack exchange,提问作者Sarthak Mittal
相关产品推荐
相关产品推荐

