/** * 获取泛型工具助手 */ public static Class<?>[] resolveTypeArguments(final Class<?> clazz, final Class<?> genericIfc) { if (null == GENERIC_TYPE_RESOLVER) { // 直接使用 spring 静态方法,减少对象创建 return SpringReflectionHelper.resolveTypeArguments(clazz, genericIfc); } return GENERIC_TYPE_RESOLVER.resolveTypeArguments(clazz, genericIfc); } 这里clazz必须继承genericIfc这泛型类吗?
是的,clazz必须继承(或实现)genericIfc这个泛型类/接口,否则这个方法将无法正确解析泛型参数。
方法的作用
这个resolveTypeArguments方法的作用是:获取clazz类在实现/继承genericIfc这个泛型类/接口时,为其类型参数提供的具体类型。
实际使用场景示例
示例1:继承泛型父类
// 泛型父类
public abstract class BaseDao<T, ID> {
// 处理T类型的实体
}
// 具体实现
public class UserDao extends BaseDao<User, Long> {
// UserDao继承BaseDao时,指定了T=User, ID=Long
}
// 调用解析
Class<?>[] typeArgs = resolveTypeArguments(UserDao.class, BaseDao.class);
// 返回结果:[User.class, Long.class]
示例2:实现泛型接口
// 泛型接口
public interface Repository<T, ID> {
T findById(ID id);
}
// 具体实现
public class UserRepository implements Repository<User, Long> {
public User findById(Long id) { /* ... */ }
}
// 调用解析
Class<?>[] typeArgs = resolveTypeArguments(UserRepository.class, Repository.class);
// 返回结果:[User.class, Long.class]
不满足条件的情况
情况1:clazz不继承genericIfc
// 没有任何关系的两个类
public class A { }
public class B { }
// 错误调用
Class<?>[] typeArgs = resolveTypeArguments(A.class, B.class);
// 可能返回null或空数组,因为没有继承关系
情况2:genericIfc不是泛型
// 非泛型接口
public interface NotGeneric { }
// 实现类
public class MyImpl implements NotGeneric { }
// 调用解析
Class<?>[] typeArgs = resolveTypeArguments(MyImpl.class, NotGeneric.class);
// 可能返回空数组,因为NotGeneric没有泛型参数
方法实现细节
让我们看下这个方法可能的实现逻辑:
public static Class<?>[] resolveTypeArguments(final Class<?> clazz, final Class<?> genericIfc) {
// 1. 验证clazz是否继承/实现了genericIfc
if (!genericIfc.isAssignableFrom(clazz)) {
throw new IllegalArgumentException(
String.format("%s is not assignable from %s",
genericIfc.getName(), clazz.getName()));
}
// 2. 通过反射获取泛型信息
Type genericSuperclass = clazz.getGenericSuperclass();
// 如果是ParameterizedType(参数化类型)
if (genericSuperclass instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
// 检查是否是我们要找的接口/父类
if (paramType.getRawType().equals(genericIfc)) {
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// 将Type转换为Class
Class<?>[] result = new Class[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; i++) {
if (actualTypeArguments[i] instanceof Class) {
result[i] = (Class<?>) actualTypeArguments[i];
}
}
return result;
}
}
// 3. 递归检查接口
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) genericInterface;
if (paramType.getRawType().equals(genericIfc)) {
// 处理类似上面的逻辑
}
}
}
return null; // 或空数组
}
实际应用场景
1. Spring框架中的依赖注入
// 在Spring中自动注入特定类型的Repository
@Autowired
private UserRepository userRepository;
// Spring内部需要知道UserRepository的泛型参数,才能进行CRUD操作
2. 通用DAO/Service实现
public abstract class BaseServiceImpl<T, ID> implements BaseService<T, ID> {
protected Class<T> entityClass;
public BaseServiceImpl() {
// 在构造器中通过工具方法获取T的实际类型
Class<?>[] typeArgs = resolveTypeArguments(
getClass(), BaseServiceImpl.class);
this.entityClass = (Class<T>) typeArgs[0];
}
}
3. MyBatis/MyBatis-Plus的通用Mapper
// 类似MyBatis-Plus的BaseMapper
public interface BaseMapper<T> {
T selectById(Serializable id);
}
// 具体Mapper
public interface UserMapper extends BaseMapper<User> {
// 其他方法
}
// MyBatis-Plus需要知道BaseMapper<T>中T的具体类型
注意事项
- 类型擦除:由于Java的类型擦除,泛型信息在运行时不可用,但通过这种方式可以在编译时保留
- 多层继承:如果有多层继承,可能需要递归查找
- 原始类型:如果使用原始类型(没有指定泛型参数),则无法解析
- 通配符:如果使用
? extends Number这样的通配符,解析会比较复杂
总结
- clazz必须继承或实现genericIfc,这是方法工作的前提
- 方法返回的是genericIfc的泛型参数在clazz中对应的具体类型
- 这是一个在框架开发中常用的技术,用于在运行时获取泛型的具体类型信息
- Spring框架的
ResolvableType就是做类似的事情,提供了更强大的泛型类型解析能力
public abstract class CrudRepository<M extends BaseMapper<T>, T> extends AbstractRepository<M, T> {
@Autowired
protected M baseMapper; 没毛病吧?
这段代码在语法上没有错误,但在设计上存在潜在问题。让我分析一下:
代码分析
public abstract class CrudRepository<M extends BaseMapper<T>, T>
extends AbstractRepository<M, T> {
@Autowired
protected M baseMapper; // 这里可能存在循环依赖
}
1. 语法正确性
- ✅ 语法完全正确
- ✅ 符合Spring自动注入规则
- ✅ 泛型定义合理
2. 潜在问题
问题1:循环依赖
// 假设有一个具体的实现
@Repository
public class UserRepository extends CrudRepository<UserMapper, User> {
// 父类CrudRepository已经注入了UserMapper
// 但UserRepository本身也是一个Repository
// 这可能导致依赖关系复杂化
}
问题2:泛型参数传递过于复杂
// 使用时的声明
public class UserRepository extends CrudRepository<UserMapper, User> {
// 必须指定两个泛型参数
// 但UserMapper本身已经携带了User的泛型信息
}
// 更好的设计可能是
public class UserRepository extends CrudRepository<User> {
// 只需要一个泛型参数
}
3. 建议的改进方案
方案1:简化泛型定义
public abstract class CrudRepository<T> extends AbstractRepository<BaseMapper<T>, T> {
@Autowired
protected BaseMapper<T> baseMapper;
// 这样使用更简洁
// public class UserRepository extends CrudRepository<User>
}
方案2:通过构造函数注入(推荐)
public abstract class CrudRepository<M extends BaseMapper<T>, T>
extends AbstractRepository<M, T> {
protected final M baseMapper;
// 通过构造函数注入,更安全
protected CrudRepository(M baseMapper) {
this.baseMapper = baseMapper;
}
}
方案3:使用Setter注入
public abstract class CrudRepository<M extends BaseMapper<T>, T>
extends AbstractRepository<M, T> {
protected M baseMapper;
@Autowired
public void setBaseMapper(M baseMapper) {
this.baseMapper = baseMapper;
}
}
4. 完整示例
当前结构
// BaseMapper接口
public interface BaseMapper<T> {
T selectById(Long id);
int insert(T entity);
int updateById(T entity);
int deleteById(Long id);
}
// UserMapper实现
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 额外的方法
List<User> selectByAge(Integer age);
}
// UserRepository
@Repository
public class UserRepository extends CrudRepository<UserMapper, User> {
// 父类已注入baseMapper
public User findUserWithDetail(Long id) {
User user = baseMapper.selectById(id);
// 其他业务逻辑
return user;
}
}
改进后的结构
// 方案1:简化泛型
public abstract class CrudRepository<T> extends AbstractRepository<BaseMapper<T>, T> {
@Autowired
protected BaseMapper<T> baseMapper;
// 通用的CRUD方法
public T findById(Long id) {
return baseMapper.selectById(id);
}
}
// 使用
@Repository
public class UserRepository extends CrudRepository<User> {
// 如果需要UserMapper的特有方法,可以额外注入
@Autowired
private UserMapper userMapper;
public List<User> findByAge(Integer age) {
return userMapper.selectByAge(age);
}
}
5. 最佳实践建议
方案A:如果不需要Mapper的特有方法
public abstract class SimpleCrudRepository<T> {
@Autowired
private BaseMapper<T> baseMapper;
// 只使用BaseMapper的通用方法
public T findById(Long id) {
return baseMapper.selectById(id);
}
}
方案B:如果需要Mapper的特有方法
public abstract class GenericCrudRepository<M extends BaseMapper<T>, T> {
private M mapper;
// 由子类提供具体的Mapper
protected GenericCrudRepository(M mapper) {
this.mapper = mapper;
}
// 可以同时使用通用和特有方法
protected M getMapper() {
return this.mapper;
}
}
// 具体Repository
@Repository
public class UserRepository extends GenericCrudRepository<UserMapper, User> {
public UserRepository(UserMapper userMapper) {
super(userMapper);
}
public List<User> findByAge(Integer age) {
return getMapper().selectByAge(age);
}
}
6. MyBatis-Plus的最佳实践
如果使用MyBatis-Plus,可以参考其官方做法:
// MyBatis-Plus风格的Service
public interface IService<T> {
// 通用服务接口
}
// 实现
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
@Autowired
protected M baseMapper;
// 实现...
}
总结
你的代码没有语法错误,但有优化空间:
- 可行但不够优雅:
CrudRepository<UserMapper, User>中的泛型信息有冗余 - 依赖注入方式:字段注入虽然简单,但不如构造器注入安全
- 设计建议:
- 如果只需要BaseMapper的通用方法,用
CrudRepository<User> - 如果需要特定Mapper的特有方法,用构造器注入
- 考虑使用MyBatis-Plus现有的Service模式
- 如果只需要BaseMapper的通用方法,用
最终建议:如果你只是做简单的CRUD,当前代码可以使用。但如果项目复杂,建议优化设计。
泛型可以spring 可以注入吗?
是的,Spring 可以注入泛型类型的Bean,而且这是Spring框架一个非常重要的特性!
Spring 泛型依赖注入的工作原理
Spring 4.0+ 支持泛型类型作为依赖注入的限定符,这意味着:
1. 基本泛型注入
// Repository定义
public class UserRepository implements Repository<User> {
// ...
}
public class ProductRepository implements Repository<Product> {
// ...
}
// Service中使用
@Service
public class UserService {
// Spring会根据泛型类型自动注入UserRepository
@Autowired
private Repository<User> repository; // ✅ 会注入UserRepository
}
2. 多层次泛型也能识别
// 多层泛型接口
public interface Repository<T> { }
public interface CrudRepository<T> extends Repository<T> { }
public class UserRepository implements CrudRepository<User> { }
// 可以这样注入
@Service
public class UserService {
@Autowired
private Repository<User> repository; // ✅ 注入UserRepository
@Autowired
private CrudRepository<User> repository; // ✅ 也注入UserRepository
}
你的代码场景分析
在你的例子中:
public abstract class CrudRepository<M extends BaseMapper<T>, T>
extends AbstractRepository<M, T> {
@Autowired
protected M baseMapper; // Spring 能正确注入
}
为什么能注入?
- 类型匹配:Spring 在创建具体的
UserRepository时,知道M = UserMapper - 泛型推断:Spring 能通过类型推导确定具体的 Bean
- 依赖查找:Spring 会查找类型为
UserMapper的 Bean 来注入
实际运行示例
完整的可运行例子
// 1. 泛型接口
public interface BaseMapper<T> {
T findById(Long id);
}
// 2. 具体Mapper
@Repository
public class UserMapper implements BaseMapper<User> {
public User findById(Long id) {
return new User(id, "test");
}
}
@Repository
public class ProductMapper implements BaseMapper<Product> {
public Product findById(Long id) {
return new Product(id, "product");
}
}
// 3. 泛型Repository
public abstract class CrudRepository<M extends BaseMapper<T>, T> {
@Autowired
protected M baseMapper; // Spring 会根据子类泛型注入
public T getById(Long id) {
return baseMapper.findById(id);
}
}
// 4. 具体Repository
@Repository
public class UserRepository extends CrudRepository<UserMapper, User> {
// 这里 baseMapper 会被自动注入 UserMapper
}
@Repository
public class ProductRepository extends CrudRepository<ProductMapper, Product> {
// 这里 baseMapper 会被自动注入 ProductMapper
}
// 5. 使用示例
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void test() {
User user = userRepository.getById(1L);
System.out.println(user);
}
}
验证Spring能正确注入的方法
方法1:在构造器中打印验证
@Repository
public class UserRepository extends CrudRepository<UserMapper, User> {
public UserRepository() {
// 测试注入
System.out.println("baseMapper类型: " + baseMapper.getClass());
// 输出: baseMapper类型: class com.example.UserMapper
}
}
方法2:编写测试用例
@SpringBootTest
class RepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void testGenericInjection() {
assertNotNull(userRepository);
// 通过反射获取baseMapper字段
Field field = CrudRepository.class.getDeclaredField("baseMapper");
field.setAccessible(true);
Object mapper = field.get(userRepository);
assertTrue(mapper instanceof UserMapper);
assertFalse(mapper instanceof ProductMapper);
}
}
Spring 泛型注入的限制
虽然Spring支持泛型注入,但有一些限制:
1. 需要具体类型信息
// ✅ 可以 - 具体类型
@Autowired
private Repository<User> userRepository;
// ❌ 不可以 - 类型擦除后就是 Repository
@Autowired
private Repository repository; // 不知道注入哪个实现
2. 运行时类型擦除问题
// Java 泛型类型擦除
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
// 运行时类型都是 List
System.out.println(stringList.getClass()); // class java.util.ArrayList
System.out.println(integerList.getClass()); // class java.util.ArrayList
// 但Spring通过ResolvableType保留了泛型信息
3. @Qualifier 结合使用
// 如果有多个相同泛型的Bean
@Repository("userRepo")
public class UserRepository implements Repository<User> { }
@Repository("productRepo")
public class ProductRepository implements Repository<Product> { }
// 使用时需要指定
@Autowired
@Qualifier("userRepo")
private Repository<User> repository;
Spring 的底层实现
Spring 通过 ResolvableType 类来处理泛型:
// 获取泛型类型信息
ResolvableType repoType = ResolvableType.forClassWithGenerics(
CrudRepository.class,
UserMapper.class,
User.class
);
// Spring在注入时使用
BeanFactory beanFactory = ...;
// 查找匹配的Bean
String[] beanNames = beanFactory.getBeanNamesForType(repoType);
最佳实践建议
1. 推荐使用构造器注入
public abstract class CrudRepository<M extends BaseMapper<T>, T> {
protected final M baseMapper;
protected CrudRepository(M baseMapper) {
this.baseMapper = baseMapper;
}
}
2. 避免过于复杂的泛型嵌套
// ❌ 过于复杂
public class Service<A extends Repository<B>, B extends Entity<C>, C> {
// 难以维护
}
// ✅ 保持简单
public class UserService {
private final UserRepository repository;
}
3. 考虑使用具体类
// 如果泛型主要是为了类型安全
public abstract class BaseCrudRepository<T> {
private final BaseMapper<T> mapper;
protected BaseCrudRepository(BaseMapper<T> mapper) {
this.mapper = mapper;
}
}
// 具体实现
@Repository
public class UserRepository extends BaseCrudRepository<User> {
public UserRepository(BaseMapper<User> mapper) {
super(mapper);
}
}
结论
你的代码完全可以正常工作:
@Autowired
protected M baseMapper; // ✅ Spring 会根据子类的泛型参数正确注入
这是因为:
- Spring 4.0+ 完整支持泛型依赖注入
- Spring 通过
ResolvableType保留了泛型信息 - 在创建子类实例时,Spring 知道具体的泛型类型
- Spring 会根据泛型类型查找匹配的 Bean 进行注入
所以放心使用,这是Spring的标准功能!