Spring注入带泛型List失败:为何用通配符可正常运行?
场景回顾
首先看你定义的泛型接口:
public interface PoiSearchDataProvider<T extends AbstractPoiSearchData> { T fetch(PoiSearchRequest poiSearchRequest) throws Exception; }
注入失败的写法:
@Resource private List<PoiSearchDataProvider<AbstractPoiSearchData>> poiSearchDataProviders;报错:
expected at least 1 bean which qualifies as autowire candidate.注入成功的写法:
@Resource private List<PoiSearchDataProvider<? extends AbstractPoiSearchData>> poiSearchDataProviders;
原因解析
Java泛型的不变性
Java泛型默认是不变类型——哪怕SubPoiData是AbstractPoiSearchData的子类,PoiSearchDataProvider<SubPoiData>也不属于PoiSearchDataProvider<AbstractPoiSearchData>的子类型,两者在泛型类型层面是完全独立的。Spring的依赖匹配逻辑
Spring处理泛型依赖注入时,会严格按照你声明的泛型类型查找Bean。当你用PoiSearchDataProvider<AbstractPoiSearchData>作为泛型参数时,Spring只会寻找**直接实现该泛型接口(泛型参数为AbstractPoiSearchData本身)**的Bean。而你的项目中实际存在的应该是实现了PoiSearchDataProvider<具体子类>的Bean(比如PoiSearchDataProvider<ShopPoiData>),这些Bean不符合严格匹配条件,所以Spring找不到候选Bean,启动报错。通配符的协变作用
改用? extends AbstractPoiSearchData属于泛型的协变声明,表示允许泛型参数是AbstractPoiSearchData的任意子类(包括本身)。此时Spring会把所有实现了PoiSearchDataProvider<T>且T继承自AbstractPoiSearchData的Bean都纳入匹配范围,自然能找到项目中存在的具体实现类,注入成功。
内容的提问来源于stack exchange,提问作者woxihuanxiayua

