Spring如何在运行时类型擦除时向泛型List<UnitMapping>注入对应实例?
Spring如何在泛型擦除下注入泛型列表?
这个问题问到点子上了——Java的泛型擦除确实会让运行时丢失具体类型信息,但Spring早就通过保留在字节码里的泛型元数据解决了这个问题,具体逻辑是这样的:
1. 泛型并没有被完全"擦除"
Java的泛型擦除只是在运行时把泛型参数替换成了Object(或者上界类型),但类的字节码中会保留泛型的签名信息(通过Signature属性)。Spring可以通过Java反射API(比如Field.getGenericType())获取到这些元数据,进而解析出泛型的实际类型参数。
2. Spring解析泛型字段的具体逻辑
当Spring扫描到MapUnitsService里的@Autowired private List<UnitMapping> unitMappings字段时:
- 它会先获取这个字段的
GenericType,得到一个ParameterizedType对象(代表带参数的泛型类型) - 调用
getActualTypeArguments()方法,取出泛型的实际参数类型——也就是UnitMapping.class - 然后去Spring容器中查找所有类型为
UnitMapping(或其子类/实现类)的Bean实例
3. 收集并注入符合条件的Bean
假设你有多个UnitMapping的实现类都被注册为Bean:
@Component public class LengthUnitMapping implements UnitMapping { /* ... */ } @Component public class WeightUnitMapping implements UnitMapping { /* ... */ }
Spring会把这两个实例全部收集起来,注入到unitMappings列表中。如果需要控制列表的顺序,可以给Bean加上@Order注解,或者让Bean实现Ordered接口,默认情况下Spring会按Bean的名称字母顺序排序。
简单理解反射解析的核心代码
Spring内部大概会做类似这样的操作(简化版):
// 获取字段的泛型类型 Field field = MapUnitsService.class.getDeclaredField("unitMappings"); ParameterizedType genericType = (ParameterizedType) field.getGenericType(); // 取出泛型参数的实际类型 Class<?> targetType = (Class<?>) genericType.getActualTypeArguments()[0]; // 从容器中获取所有该类型的Bean List<Object> beans = applicationContext.getBeansOfType(targetType).values().stream().collect(Collectors.toList()); // 注入到字段中 field.set(mapUnitsServiceInstance, beans);
总结一下:Spring并没有绕过泛型擦除,而是利用了Java字节码中保留的泛型元数据,通过反射解析出泛型参数的实际类型,从而精准找到需要注入的Bean集合。
内容的提问来源于stack exchange,提问作者rogergl




