泛型的作用
在JDK1.5开始JAVA就引入了泛型,泛型的引入,能够减少我们在运行时的很多错误,让错误在编译期间就被发现。例如定义一个列表:List list = new ArrayList(); 我们可以往这个列表添加数值类型,同时也可以往列表添加字符串,在编译期间编译器不会给我们错误的提示。但在运行时,程序就会出错。当我们使用泛型定义列表:List
参数泛型化
下面先给一段代码。
我们先定义一个用户类User:
1 |
|
如果我们想通过用户名或者昵称来查询用户信息,在Controller里我们可以这样做:
1 | "/query",method = GET) (value = |
我们知道,当我们在发起HTTP请求的时候,如果我们传递username或者nickName参数,Spring会自动帮我们将值绑定到user中,我们在query方法中直接使用就行,这个很容易。
接下来,我们想做一下分页功能,几乎每一个web站点都离不开分页。我们想查某一个国家的用户,并分页显示。要完成分页功能,首先我们定义一个分页类型.
1 | public class Page<P> { |
在分页对象里,我们定义了一个泛型P,因为我们想通过P来接收前端传过来的查询参数。并且,我们并不知道这个P的具体类型是什么。可能不同的业务逻辑里,这个参数类型实体是不同的。
很显示,我们分页查询,希望这样做:
1 | "/queryPage",method = GET) (value = |
我们希望,在发起http请求时带上page=1,size=15,param.addr=’中国’,服务端会返回15个中国用户的信息。但是结果却让我们大跌眼镜,事情并没有往我们想的方向发展,addr没能正确绑定到User里。
为什么呢?最主要原因是因为泛型运行时擦除的原因。在运行时,Spring无法知道Page的泛型类型是什么,只能用Object来初始化,而要将addr绑定到Object里,显然不行。
如何解决
很多时候,参数的泛型化是不可避免的,泛型能让代码更加优雅。当然,上面的例子我们可以避开泛型化处理分页也是很简单的。我们如何可以将数据绑定到泛型里呢?首先我们来了解一下Spring的数据绑定逻辑。
下面是Spring的ModelAttributeMethodProcessor类的源码:
1 | public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { |
这里不具体分析源码,我们知道,该Processor通过supportsParameter判断是否要做数据绑定,通过resolveArgument来处理数据绑定。我们可以自定义一个Processor,来影响Spring的数据绑定逻辑。
首先,我们定义一个注解,该注解用于指定泛型的具体类型:
1 | (ElementType.PARAMETER) |
我们再定义一个Processor,用于实现数据绑定:
1 | /** |
要让该Processor起作用,需要添加到Spring中,这里用了Spring boot,实现如下:
1 |
|
此时,Controller就可以这样定义了:
1 | "/queryPage",method = GET) (value = |
当我们发起请求时,主要参数带有param.addr,地址就可以正确绑定到泛型里的User对象下。