IT学习站-137zw.com

作者: 向往草原403
查看: 146|回复: 0

more +资源更新Forums

more +随机图赏Gallery

网易课堂马丁的Illustrator(Adobe AI)大师课,90节完整版网易课堂马丁的Illustrator(Adobe AI)大师课,90节完整版
价值9999元 筑龙造价全专业(20191214_114251)精品课程推荐价值9999元 筑龙造价全专业(20191214_114251)精品课程推荐
微专业 - Java高级开发工程师(完整版)微专业 - Java高级开发工程师(完整版)
曾奇峰的心理课精神分析30讲,带你深入潜意识,解密你不...曾奇峰的心理课精神分析30讲,带你深入潜意识,解密你不...
词霸天下之3万词汇速记进阶 入门到精通彻底攻克英语完整版词霸天下之3万词汇速记进阶 入门到精通彻底攻克英语完整版
談鋼琴!?我最大咖!郎朗钢琴大师课~入门到进阶 完整版...談鋼琴!?我最大咖!郎朗钢琴大师课~入门到进阶 完整版...

Spring boot 自定义 Resolver 支持 interface 类型参数

Spring boot 自定义 Resolver 支持 interface 类型参数

[复制链接]
向往草原403 | 显示全部楼层 发表于: 2019-11-14 10:55:01
向往草原403 发表于: 2019-11-14 10:55:01 | 显示全部楼层 |阅读模式
查看: 146|回复: 0
在编写 RestController 层的代码时,由于数据实体类定义了接口及实现类,本着面向接口编程的原则,我使用了接口作为 RestController 方法的入参。
代码大致如下(省略具体业务部分):
(1)模型接口:
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code(2)模型实现类
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code(3)RestController POST接口代码
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code(4)前台用的axios发送的请求代码
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code
但在运行测试时发现 Spring boot 本身的默认中并不支持将interface或抽象类作为方法的参数。报了如下错误:大致意思时不存在创建实例的构造函数,抽象类型需要配置映射到具体的实现类。

解决方案一:
于是我上网搜了下解决方法,最终在 StackOverflow 上找到一种解决方案:
通过添加注解的方式,将接口映射到实现类。

这种方法可以解决方法入参为接口的问题,但同时又会引入一个问题:接口和实现类相互引用,导致循环依赖。而且如果我有很多数据类的接口及实现类的话,每个接口都要写一遍注解。
于是继续探索。。。

解决方案二:
继承 HandlerMethodArgumentResolver  接口实现里面的 supportsParameter  和  resolveArgument 方法。
(1)在supportsParameter 方法中返回支持的类型。其中MODEL_PATH为实体类的包路径,下列代码中默认支持了包内的所有类型。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code(2)在 resolveArgument 方法中,通过反射生成一个实现类的对象并返回。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code(3)最后添加到Spring boot 的配置中
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code
方案二可以解决找不到构造函数的问题,运行不会报错,也不会导致循环依赖,但却没法将前台的数据注入到入参对象中。也就是给方法传入的只是一个刚new出来的UserImpl 对象。

经过测试发现,虽然对post请求无法注入前台数据,但对于get请求,还是可以的:
前台get方法代码:后台get方法代码:
解决方案三:
由于在网上没有找到好的解决方案,我最后通过看Spring boot 源码 + 调试跟踪 + 写demo尝试的方式,终于找到了好的解决方案。
这里先分享下大致的思路:
(1)Spring boot的相关代码应该在 HandlerMethodArgumentResolver 接口对应的包里或者附近。但这样找还是比较慢,因为代码还是很多。

(2)通过打断点,看看哪里调用了 public boolean supportsParameter(MethodParameter parameter) 方法。
于是找到了HandlerMethodArgumentResolverComposite 类调用的地方:
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 855030-20190908205505775-1340993131

  从上图可以看到,当前处理的是第一个参数HttpSession。

(3)先将controller方法的入参先改为UserImpl,也就是实现类,在步骤(2)的截图对应的代码中打断点。
继续调试,找到Spring boot 解析被@RequestBody 注解标注的参数UserImpl user 的时候,用的是什么Resolver。
如下图所示,调用Evaluate窗口获取类型信息,点击 Navigate 跳转到对应的类 RequestResponseBodyMethodProcessor。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 855030-20190908211043128-1693947271


(4) RequestResponseBodyMethodProcessor 类中的 resolveArgument 方法源码如下:
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code回到最初的问题,导致无法传入interface类型参数的原因是接口无法实例化。那既然如此,我们要修改的地方肯定是Spring boot 尝试实例化接口的地方,也就是实例化失败进而抛出异常的地方。
一路顺腾摸瓜,最终发现 readWithMessageConverters 方法中, 通过给 readWithMessageConverters 方法传入类型信息,最终生成参数实例。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 855030-20190908212458206-88922894


(5) 从(4)中可以看到,相关方法的访问级别为 protected,也就是我们可以通过继承 RequestResponseBodyMethodProcessor 并覆写 readWithMessageConverters 即可。
通过反射,注入 User 接口的实现类型 UserImpl 的class:

Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code
完成上面的代码后,跑了一下,发现并没有什么用,报的错误还是跟最开始的一样。
由此推测,应该是Spring boot 默认配置的 Resolver的优先级比较高,导致我们自定义的并没有生效。
于是继续查找原因,发现自定义的Resolver的优先级几乎垫底了,在远未调用到之前就被它的父类抢了去。

(6)提高自定义 Resolver的优先级。
一个可行的方法是:在Spring boot 框架初始化完成后,获取到所有的Resolver,然后将自定义的加在ArrayList的前面。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ContractedBlock
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 ExpandedBlockStart
View Code
值得注意的是,getResolvers()方法返回的是不可更改的List,不能直接插入。
Spring boot 自定义 Resolver 支持 interface 类型参数  技术博客 855030-20190908214739689-1596989254


至此,自定义参数处理器就可以解析RestController标注的类中的方法的 interface类型参数了。

如果要支持其他类型(比如抽象类、枚举类),或者使用自定义注解标注入参,也可以通过类似的方法来实现。

本文地址:https://www.cnblogs.com/laishenghao/p/11488724.html


来源:http://www.137zw.com
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
137zw.com IT学习站致力于免费提供精品的java技术教程和python技术教程,CCNA书籍/资料/CCNP书籍/资料教程/CCIE书籍/资料/H3C学习/认证/一级建造师考试/微软学习/认证/包括基础教程和高级实战教程,同时也提供分享网站源码下载和互联网相关一系列的技术教程,我们想做的就是让知识分享更有价值!(IT学习站官方唯一域名地址:www.137zw.com 请谨防假冒网站!)本站所有资源全部收集于互联网或网友自行分享,分享目的仅供大家学习与参考,如无意中侵犯您的合法权益,请联系本站管理员进行删除处理!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

浙ICP备19022368号-1|Archiver|手机版|IT学习站-137zw.com

GMT+8, 2020-7-4 18:01 , Processed in 0.292220 second(s), 33 queries .

快速回复 返回顶部 返回列表