读书笔记-Spring中更好的Java泛型操作API-ResolvableType

简介: 读书笔记-Spring中更好的Java泛型操作API-ResolvableType

随着泛型用的越来越多,获取泛型实际类型信息的需求也会出现,如果用原生API,需要很多步操作才能获取到泛型,比如:

ParameterizedType parameterizedType =
(ParameterizedType) ABService.class.getGenericInterfaces()[0];
Type genericType = parameterizedType.getActualTypeArguments()[1];

Spring提供的ResolvableType API,提供了更加简单易用的泛型操作支持,如:

ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class);
resolvableType1.as(Service.class).getGeneric(1).resolve()

对于获取更复杂的泛型操作ResolvableType更加简单。

假设我们的API是:

public interface Service<N, M> {
}
@org.springframework.stereotype.Service
public class ABService implements Service<A, B> {
}
@org.springframework.stereotype.Service
public class CDService implements Service<C, D> {
}

① 得到类型的泛型信息

ResolvableType resolvableType1 = ResolvableType.forClass(ABService.class);

通过如上API,可以得到类型的ResolvableType,如果类型被Spring AOP进行了CGLIB代理,请使用 ClassUtils.getUserClass(ABService.class) 得到原始类型。

可以通过如下得到泛型参数的第1个位置(从0开始)的类型信息

resolvableType1.getInterfaces()[0].getGeneric(1).resolve()

因为我们泛型信息放在 Service 上,所以需要resolvableType1.getInterfaces()[0]得到。通过getGeneric(泛型参数索引)得到某个位置的泛型;resolve()把实际泛型参数解析出来。


② 得到字段级别的泛型信息

假设我们的字段如下:

@Autowired
private Service<A, B> abService;
@Autowired
private Service<C, D> cdService;
private List<List<String>> list;
private Map<String, Map<String, Integer>> map;
private List<String>[] array;

通过如下API可以得到字段级别的ResolvableType

ResolvableType resolvableType2 =
ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class,
"cdService"));

然后通过如下API得到Service的第0个位置上的泛型实参类型,即C

resolvableType2.getGeneric(0).resolve()

比如 List> list;是一种嵌套的泛型用例,我们可以通过如下操作获取String类型:

ResolvableType resolvableType3 =
ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class,
"list"));
resolvableType3.getGeneric(0).getGeneric(0).resolve();

更简单的写法:

resolvableType3.getGeneric(0, 0).resolve(); //List<List<String>> 即String

比如Map> map;我们想得到Integer,可以使用:

ResolvableType resolvableType4 =
ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class,
"map"));
resolvableType4.getGeneric(1).getGeneric(1).resolve();

更简单的写法:

resolvableType4.getGeneric(1, 1).resolve()

③ 得到方法返回值的泛型信息

假设我们的方法如下:

private HashMap<String, List<String>> method() {
  return null;
}

得到Map中的List中的String泛型实参:

ResolvableType resolvableType5 =
ResolvableType.forMethodReturnType(ReflectionUtils.findMethod(GenricInjectTest.c
lass, "method"));
// 直接简化写法
resolvableType5.getGeneric(1, 0).resolve();

④ 得到构造器参数的泛型信息

假设我们的构造器如下:

public Const(List<List<String>> list, Map<String, Map<String, Integer>> map) {
}

我们可以通过如下方式得到第2个参数( Map>)中的Integer:

ResolvableType resolvableType6 =
ResolvableType.forConstructorParameter(ClassUtils.getConstructorIfAvailable(Cons
t.class, List.class, Map.class), 1);
resolvableType6.getGeneric(1, 0).resolve();

⑤ 得到数组组件类型的泛型信息

如对于private List[] array; 可以通过如下方式获取List的泛型实参String:

ResolvableType resolvableType7 =
ResolvableType.forField(ReflectionUtils.findField(GenricInjectTest.class,
"array"));
resolvableType7.isArray();//判断是否是数组
resolvableType7.getComponentType().getGeneric(0).resolve();

⑥ 自定义泛型类型

ResolvableType resolvableType8 = ResolvableType.forClassWithGenerics(List.class,
String.class);
ResolvableType resolvableType9 =
ResolvableType.forArrayComponent(resolvableType8);
resolvableType9.getComponentType().getGeneric(0).resolve();

ResolvableType.forClassWithGenerics(List.class, String.class)相当于创建一个List类型;

ResolvableType.forArrayComponent(resolvableType8);:相当于创建一个List[]数组;

resolvableType9.getComponentType().getGeneric(0).resolve():得到相应的泛型信息;


⑦ 泛型等价比较

如下判断两个类型是否属于同一类:

resolvableType7.isAssignableFrom(resolvableType9)

如下创建一个List[]数组,与之前的List[]数组比较,将返回false。

ResolvableType resolvableType10 = ResolvableType.forClassWithGenerics(List.class,
Integer.class);
ResolvableType resolvableType11=
ResolvableType.forArrayComponent(resolvableType10);
resolvableType11.getComponentType().getGeneric(0).resolve();
resolvableType7.isAssignableFrom(resolvableType11);

从如上操作可以看出其泛型操作功能十分完善,尤其在嵌套的泛型信息获取上相当简洁。目前整个Spring4环境都使用这个API来操作泛型信息。

如之前说的泛型注入:Spring4新特性——泛型限定式依赖注入,通过在依赖注入时使用如下类实现

GenericTypeAwareAutowireCandidateResolver
QualifierAnnotationAutowireCandidateResolver
ContextAnnotationAutowireCandidateResolver

还有如Spring的核心BeanWrapperImpl,以及整个Spring/SpringWevMVC的泛型操作都是替换为这个API了:GenericCollectionTypeResolver和GenericTypeResolver都直接委托给ResolvableType这个API。

⑧ 泛型限定式依赖注入

以前我们常见的repository:

public abstract class BaseRepository<M extends Serializable> {
  public void save(M m) {
    System.out.println("=====repository save:" + m);
  }
}
@Repository
public class UserRepository extends BaseRepository<User> {
}
@Repository
public class OrganizationRepository extends BaseRepository<Organization> {
}

对于Repository,我们一般是这样实现的:首先写一个模板父类,把通用的crud等代码放在BaseRepository;然后子类继承后,只需要添加额外的实现。

以前我们常见的service写法

public abstract class BaseService<M extends Serializable> {
  private BaseRepository<M> repository;
  public void setRepository(BaseRepository<M> repository) {
    this.repository = repository;
  }
  public void save(M m) {
    repository.save(m);
  }
}
@Service
public class UserService extends BaseService<User> {
  @Autowired
  public void setUserRepository(UserRepository userRepository) {
    setRepository(userRepository);
  }
}
@Service
public class OrganizationService extends BaseService<Organization> {
  @Autowired
  public void setOrganizationRepository(OrganizationRepository
  organizationRepository) {
    setRepository(organizationRepository);
  }
}

可以看到,以前必须再写一个setter方法,然后指定注入的具体类型,然后进行注入。

泛型service的写法:

public abstract class BaseService<M extends Serializable> {
  @Autowired
  protected BaseRepository<M> repository;
  public void save(M m) {
    repository.save(m);
  }
}
@Service
public class UserService extends BaseService<User> {
}
@Service
public class OrganizationService extends BaseService<Organization> {
}

大家可以看到,现在的写法非常简洁。支持泛型式依赖注入。这样对于那些基本的CRUD式代码,可以简化更多的代码。

如果大家用过Spring data jpa的话,以后注入的话也可以使用泛型限定式依赖注入 :

@Autowired
private Repository<User> userRepository;

对于泛型依赖注入,最好使用setter注入,这样万一子类想变,比较容易切换。如果有多个实现时,子类可以使用@Qualifier指定使用哪一个。


目录
相关文章
|
23天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
36 4
|
22天前
|
缓存 Java API
基于Spring Boot REST API设计指南
【10月更文挑战第11天】 在构建现代Web应用程序时,RESTful API已成为一种标准,使得不同的应用程序能够通过HTTP协议进行通信,实现资源的创建、读取、更新和删除等操作。Spring Boot作为一个功能强大的框架,能够轻松创建RESTful API。本文将详细介绍如何在Spring Boot中设计和实现高质量的RESTful API。
115 61
|
6天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
14天前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
36 1
Spring 框架:Java 开发者的春天
|
1天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
14 4
|
10天前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
14天前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。
|
16天前
|
Java 大数据 API
别死脑筋,赶紧学起来!Java之Steam() API 常用方法使用,让开发简单起来!
分享Java Stream API的常用方法,让开发更简单。涵盖filter、map、sorted等操作,提高代码效率与可读性。关注公众号,了解更多技术内容。
|
13天前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
12 0
[Java]泛型
|
14天前
|
JSON Java Maven
实现Java Spring Boot FCM推送教程
本指南介绍了如何在Spring Boot项目中集成Firebase云消息服务(FCM),包括创建项目、添加依赖、配置服务账户密钥、编写推送服务类以及发送消息等步骤,帮助开发者快速实现推送通知功能。
32 2