如何一个VO实体在多个接口中返回的字段不一样呢?
假设现在有一张user表,包含id、name、pwd、area四个字段,有两个接口:
- 用户列表
- 用户详情
- 在用户列表中,只展示\`id\`/\`name\`/\`area\`三个字段;
- 在详情中,展示全部字段。
环境:springboot项目。
现在我配置了\`spring.jackson.default-property-inclusion=NON\_NULL\`。
P1:在列表接口中,一旦\`area\`字段为空,返回的字段就参差不齐,有的返回2个字段,有的返回三个字段,请问如何解决这样的问题?
P2:在详情接口中,也会出现这样的问题,当\`area\`为\`null\`时,详情返回3个字段;否则返回4个字段。
Q:请问如何让接口返回固定的字段,例如,列表接口中,固定返回3个字段,即使\`area\`为空,也要保留该字段。同理,详情接口中,固定返回4个字段,即使有字段为空也要保留该字段。
问题出现的环境背景及自己尝试过哪些方法
我尝试改配置\`spring.jackson.default-property-inclusion\`,
但是改了好多个都没有达到我的要求。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
起初第一眼看到题主的问题,我是有点匪夷所思的。。。主要是下面这段话
写接口,固定字段返回,看起来都很正常,那解决方案不就是
UserListVO
,里面有3个字段UserDetailVO
,里面有4个字段这跟
jackson
配置null
是否返回,毫无关系啊,jackson
只是一个序列化工具,但是不同接口返回不同字段这是业务逻辑,就需要创建类才可以解决啊。创建两个类真的就是很难了么。。。那为啥非要只用一个
UserVO
对象来表示两个不同接口的返回呢?假如以后新增还有其他和User
相关的接口,是不是要把再在UserVO
类中塞满一堆字段,然后不同接口返回不同个数的字段呢,这样索性不要UserVO
得了,直接实体对象User
返回算了嘛。诚然,就算是按照myskies的方式做了分组,这是一种解决题主问题的方案,但是但是,如果是实际公司业务开发,我想没有哪个兄弟愿意接手在一个大而全的
VO
对象去找他们需要的分组叭。。。起码要是我,我是要哭的,字段少还好,字段多了。。。@JsonView
中各种取值是要看花了的哈不过吐槽归吐槽,上面只是我作为同为程序员角色,站在同一角度提出的小意见,但是如果作为思否中问题和回答问题的角色,也就是相当于甲方乙方的角度来说的话,我还是要来想办法解决题主的问题
首先还是先捋一下当前的思路,以及需要解决的问题
假设现在就是
UserService
中的两个方法返回虽然是两个方法,但是都是返回的同一个关联类
UserVO
的对象,然而又需要不同字段的返回结果。同一个类,但是不同方法返回不同的字段。你细品。。。不对啊,这不可能啊,字段是跟Class
相关的,而Class
本身就是独一份,咋能说运行时变来变去呢,毕竟UserVO
的对象可是需要Class
来实例化的啊所以如果按照上面那种写法是根本不可能的,所以必须是
list()
和detail()
要返回不同的类,但是之前已经感受到了甲方的需求了,就是不想多创建两个类,那咋办?当然就是回到
Map
啦,万能Map
,毕竟Map
的数据结构跟POJO
的对象很类似嘛,都是键值对。因此
UserService
就变成了啊~多么朴素的写法啊(谁在我小组这么写,我就要杀人了啊!!)
那再看看实现,都这么返回
Map
,那还不无法无天,想怎么写就怎么写咯,当然这里用到了CGLB
的BeanMap
,spring
自己把CGLIB
也打入到项目中了,所以可以直接用Spring
的BeanMap
,用BeanMap
直接把UserVO
转换成Map
所以
UserService
的实现类UserServiceImpl
可能是这样写的虽然这种方案确实能解决问题,但是估计甲方会把我头打爆,这让甲方改多少东西,多少逻辑,而且如果需要扩展的话,比如用户详情
detail()
中突然不返回id
了,岂不是还要在detail()
方法体中增加remove
的操作,实在太不开闭原则了因此回归甲方需求的本身,那就是希望在
detail()
和list()
方法主体不进行太大改动的基础上实现功能,好歹也是什么方法返回什么字段应该是可配置的吧,这样后续修改也很容易,不用改方法主体,改配置就可以了所以,
Spring AOP
+注解,上。题主
UserService
的实现类UserServiceImpl
估计是这样写的现在要控制不同方法返回的
UserVO
字段不一样,先不说咋实现,咱们先把咋配置整好,直接整一个注解配置上。所以注解FieldLimit
应运而生,就一个属性,配置需要返回哪些字段的名称用
FieldLimit
把我们的方法装饰一下那接下来就是怎么实现了,显然刚才提到了
AOP
,但是AOP
只是一个桥梁,是一个把处理不同字段的逻辑移到切面的一个桥梁,真正到底怎么实现不同方法返回不同的字段,仅仅靠AOP
是解决不了,就像最开始我提到的,这个本质就是不同的业务,其实是需要两个不同返回对象来处理的,但是但是呢,甲方懒了亿点点,所以需要我们来用一些好工具帮他实现即:我们需要帮甲方生成两个类,两个类的字段是有限制的,限制来源于
FieldLimit
(当然由于现在题主提到的需求中,detail()方法是返回所有字段的,所以准确来说只是生成一个类)咋运行时生成类呢,当然用咱们的字节码编程啦。借助偏友好点
API
的用Javassist
嘛(主要前段时间才接触过,哈哈哈)由于本质上不同方法返回的类肯定是不一样的,所以就不能写返回
UserVO
了,得改成一个通用的,可以用Object
,但是那也太抽象了,所以干脆做一个标记接口,返回接口,然后我们生成的新类去实现这个接口就可以了有了这个接口,这时候
UserService
就变成了由于接口发生了变化,当然实现类也要做修改,不过不用改动太大,只是改改返回的类型,然后记得给
UserVO
实现接口IBaseVO
即可那接下来就是最关键的地方了,怎么用切面实现生成新类的效果,直接上代码,切面类
FieldLimitAspect
emmm,由于我不想写死很多东西,所以
FieldLimitAspect
并没有把所有的逻辑包含进去,仅仅是包含了如何对方法返回的处理结果做类型判断和对注解FieldLimit
的值的处理的主干过程,具体怎样构造新的Class
,怎样创建新的对象,都在其中的handler
中,也就是接口ReturnTypeHandler
。方便后续的可插拔式处理。现在
ReturnTypeHandler
支持处理UserVO[]
,对应的handler
是ReturnTypeCollectionHandler
List<UserVO>
,对应的handler
是ReturnTypeArrayHandler
POJO
对象,例如UserVO
,对应的handler
是ReturnTypePOJOHandler
如果你以后还想支持
Map
也可以再去添加一个ReturnTypeHandler
实现类即可。不过你需要对于Java
的java.lang.reflect.Type
以及Spring
的ResolvableType
也一些了解才比较好写。所有实现类都在这里github上,你可以慢慢看
当然当然,不知道刚刚第一次提到注解
FieldLimit
,我给予注解的@Retention
是RetentionPolicy.RUNTIME
,也就是运行时也保留,因此我下面的方案才是Spring AOP
+注解,但是我也在
FieldLimit
中提到,其实也可以是RetentionPolicy.SOURCE
,也就是注解是在编译时才有效。那最终的方案就不是Spring AOP
+注解,毕竟编译时那还没有Spring
啥事呢。那就走我们的APT(Annotation Processing Tool)
。就是Lombok
类似,不过甲方的这里的情况要比Lombok
的场景稍微难点(个人感觉),毕竟Lombok
已经是偷偷修改了AST
(抽象语法树),但是修改也是加方法而已,是增量,但是甲方这里的需求是直接需要修改以前的类的返回,自己以前也仅仅是做过新增类,就是auto-constants,它是根据POJO
对象新增一个专门存放其属性名的类,这样就不用老写了死代码了,如果修改了属性,对应的地方不修改,是要报错的,这都还没有做到修改AST
,但是给我的感觉是可以做的,不过估计要比我提供的Spring AOP
+注解还要麻烦点,算是提供一种思路了不过最后说下来,题主也可以看到,其实明明新增两个对应业务
POJO
对象就解决问题了,但是这个也太懒了叭。。。ResponseBodyAdvice这个接口很强大,在你转json之前,会回调beforeBodyWrite这方法,你可以修改接口返回的数据,问题主要是在哪勒,程序不知道你这次调用到底需要哪些字段,这个得调用接口的时候,就传入相关参数过来
Spring提供了JsonView来解决当前问题:
控制器:
希望能解决你的当前问题。
以下是关于评论中的问题。
空对象问题,假设B中有A,此时A如果为null,则将返回null。首先我们说这样设计当然是没有任何问题的。但如果你不想主上他返回null,则可以在B中重写下getA():