我说是泛型类型注入到当前类 @autowired T mapper
Spring 泛型字段注入原理
您问的是当在类中直接注入泛型字段的情况,比如 @Autowired T mapper。这涉及到 Spring 的泛型依赖注入机制,让我详细解释。
1. 基本情况
JAVA
@Component
public class MyService<T> {
@Autowired
private T mapper; // ❌ 这不会正常工作
}
直接注入泛型类型参数是不行的,因为:
- 泛型在运行时被擦除,Spring 无法确定具体的类型
- 泛型类型参数
T在运行时是Object类型
2. Spring 处理泛型依赖的原理
Spring 注入依赖时,需要知道具体的类型。让我们看几种情况:
2.1 情况一:在泛型基类中注入泛型字段
JAVA
// 基类
public abstract class BaseService<M extends BaseMapper<T>, T> {
@Autowired
protected M baseMapper; // ✅ 这可以工作,但需要特殊处理
}
这看起来像是 Spring 的神奇之处,但实际上需要特定的条件才能工作。
3. 如何实现泛型字段注入
3.1 方式一:通过子类确定具体类型
JAVA
// 1. 泛型基类
public abstract class BaseService<M, T> {
@Autowired
protected M mapper; // Spring 在子类具体化时才能确定类型
public abstract T getById(Long id);
}
// 2. 具体子类
@Service
public class UserService extends BaseService<UserMapper, User> {
// UserService 被实例化时,Spring 知道:
// M = UserMapper, T = User
// 然后会注入 UserMapper
@Override
public User getById(Long id) {
return mapper.selectById(id);
}
}
关键点:当 UserService 被 Spring 管理时,Spring 会:
- 解析
UserService的泛型信息 - 知道
BaseService<UserMapper, User>中的M是UserMapper - 查找类型为
UserMapper的 Bean 注入
3.2 方式二:通过构造函数注入
JAVA
// 泛型基类
public abstract class BaseService<M, T> {
protected final M mapper;
public BaseService(M mapper) {
this.mapper = mapper;
}
}
// 具体子类
@Service
public class UserService extends BaseService<UserMapper, User> {
@Autowired
public UserService(UserMapper mapper) {
super(mapper); // 将具体类型传递给父类
}
}
4. MyBatis-Plus 的实现方式
MyBatis-Plus 的 ServiceImpl 实际上没有使用 @Autowired 注入 mapper,它使用了不同的机制。
4.1 MyBatis-Plus 的实际实现
查看 MyBatis-Plus 源码:
JAVA
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Autowired
protected M baseMapper; // 这里确实有 @Autowired
// 其他代码...
}
4.2 为什么这能工作?
当有具体子类时:
JAVA
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> {
// UserServiceImpl 继承时指定:
// M = UserMapper, T = User
// 所以基类中的 baseMapper 字段类型是 UserMapper
}
Spring 在创建 UserServiceImpl 的 Bean 时:
- 解析
UserServiceImpl的泛型信息 - 发现它继承
ServiceImpl<UserMapper, User> - 知道基类的
baseMapper字段类型是UserMapper - 在容器中查找
UserMapper类型的 Bean - 注入到
baseMapper字段
5. Spring 的泛型解析过程
5.1 Bean 定义解析
JAVA
// 1. 扫描时发现
@Component
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
// 2. 创建 BeanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(UserServiceImpl.class);
// 3. 解析泛型父类
ResolvableType superType = ResolvableType.forClass(UserServiceImpl.class)
.getSuperType(); // ServiceImpl<UserMapper, User>
// 4. 获取泛型参数
ResolvableType mapperType = superType.getGeneric(0); // UserMapper
ResolvableType entityType = superType.getGeneric(1); // User
5.2 依赖注入时的类型匹配
JAVA
// 在 DefaultListableBeanFactory 中
private Object resolveField(String fieldName, Object bean, BeanDefinition bd) {
Field field = getField(fieldName, bean.getClass());
// 获取字段的类型(包含泛型)
ResolvableType fieldType = ResolvableType.forField(field, bean.getClass());
// 对于 UserServiceImpl 的 baseMapper 字段:
// fieldType = UserMapper
// 在容器中查找匹配的 Bean
String[] beanNames = getBeanNamesForType(fieldType);
// 找到 UserMapper 的 Bean
return getBean(beanNames[0]);
}
6. 泛型字段注入的限制
6.1 不能直接在泛型类中注入
JAVA
// ❌ 这不会工作
@Component
public class GenericService<T> {
@Autowired
private T repository; // Spring 不知道 T 的具体类型
public T getById(Long id) {
return repository.findById(id); // 编译错误
}
}
6.2 需要具体化的子类
JAVA
// ✅ 通过子类具体化才能工作
public abstract class BaseRepository<T> {
// 可以有非泛型字段
@Autowired
private DataSource dataSource;
}
// 具体子类
@Repository
public class UserRepository extends BaseRepository<User> {
// 这里可以注入特定的依赖
@Autowired
private UserCache userCache;
}
7. 替代方案
7.1 使用方法注入
JAVA
@Component
public class GenericService<T> {
// 通过方法注入具体类型
protected T getRepository(Class<T> repositoryClass) {
return applicationContext.getBean(repositoryClass);
}
}
7.2 使用类型令牌模式
JAVA
@Component
public class GenericService<T> {
private final T repository;
public GenericService(Class<T> repositoryClass) {
this.repository = applicationContext.getBean(repositoryClass);
}
}
8. 完整示例
8.1 定义泛型 Repository
JAVA
// 泛型 Repository 接口
public interface BaseRepository<T, ID> {
T findById(ID id);
List<T> findAll();
}
// 具体实现
@Repository
public class UserRepository implements BaseRepository<User, Long> {
// 实现...
}
8.2 泛型 Service
JAVA
// 泛型 Service
public abstract class BaseService<R extends BaseRepository<T, ID>, T, ID> {
@Autowired
protected R repository; // 这里能注入是因为子类具体化了 R
public T getById(ID id) {
return repository.findById(id);
}
}
8.3 具体 Service
JAVA
@Service
public class UserService extends BaseService<UserRepository, User, Long> {
// 自动注入 UserRepository
// 因为父类的 R 被具体化为 UserRepository
public void someMethod() {
User user = getById(1L); // 调用父类方法
}
}
9. Spring 内部处理源码
9.1 字段注入源码
JAVA
// AutowiredAnnotationBeanPostProcessor
@Override
public PropertyValues postProcessProperties(PropertyValues pvs,
Object bean, String beanName) {
// 找到所有需要自动注入的字段
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
// 注入字段
metadata.inject(bean, beanName, pvs);
return pvs;
}
// 在 inject 方法中
protected void inject(Object bean, @Nullable String beanName,
@Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
// 获取字段的类型(包含泛型)
ResolvableType type = ResolvableType.forField(field, bean.getClass());
// 根据类型查找 Bean
Object value = beanFactory.resolveDependency(
new DependencyDescriptor(field, true), beanName);
// 反射设置字段值
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
9.2 泛型类型解析
JAVA
// 解析字段类型的泛型信息
private ResolvableType getResolvableType(Field field, Object bean) {
// 获取字段声明的类型
ResolvableType type = ResolvableType.forField(field);
// 如果字段是泛型,需要结合 bean 的实际类型
if (type.hasGenerics()) {
// 获取 bean 的实际类型
ResolvableType beanType = ResolvableType.forClass(bean.getClass());
// 解析字段在 bean 中的实际泛型类型
type = type.as(beanType.getType());
}
return type;
}
10. 注意事项
10.1 泛型擦除的影响
JAVA
// 运行时,泛型信息可能丢失
public class ServiceImpl<M, T> {
@Autowired
private M mapper; // 运行时类型是 Object
// 但 Spring 在注入时能通过子类恢复泛型信息
}
10.2 多个候选 Bean 问题
JAVA
// 如果有多个相同泛型的实现
@Repository
public class UserRepository1 implements BaseRepository<User, Long> { }
@Repository
public class UserRepository2 implements BaseRepository<User, Long> { }
// 注入时会报错:有多个候选
// 需要添加 @Qualifier
public class UserService extends BaseService<UserRepository1, User, Long> {
// 或者
@Autowired
@Qualifier("userRepository1")
protected BaseRepository<User, Long> repository;
}
11. 总结
Spring 的泛型字段注入原理:
- 不是直接注入泛型参数:不能直接注入
@Autowired T mapper - 需要具体化的子类:泛型字段必须在具体子类中确定具体类型
- 运行时泛型恢复:Spring 通过子类的泛型信息恢复父类字段的具体类型
- 依赖查找机制:Spring 在注入时会根据字段的类型(包含泛型)查找匹配的 Bean
关键理解:
- 泛型字段注入能够工作,是因为子类继承时提供了具体的类型参数
- Spring 在创建 Bean 时,能解析类的继承结构中的泛型信息
- 注入时,Spring 会根据字段在具体子类中的实际类型来查找 Bean
这就是为什么 MyBatis-Plus 的 ServiceImpl 中的 @Autowired protected M baseMapper 能够正常工作的原因。当有具体子类如 UserServiceImpl extends ServiceImpl<UserMapper, User> 时,Spring 知道 M 的具体类型是 UserMapper,然后注入 UserMapper 的 Bean。