做一个消息中心,专门负责发送消息。消息分为几种渠道,包括手机通知(Push)、短信(SMS)、邮件(Email),Websocket等渠道。
我定义了一个基类MessageRequest用来接收请求参数,代码如下:
public class MessageRequest implements Serializable { protected MessageChannel channel; private MessageRequest(){} protected MessageRequest(MessageChannel channel){ this.channel = channel; } public MessageChannel getChannel() { return this.channel; }}在MessageRequest中有个属性channel是枚举MessageChannel,该枚举列举所有渠道,代码如下:
public enum MessageChanne { PUSH, EMAIL, WEBSOCKET, SMS, ; MessageChannel() {}}MessageRequest有各种渠道的子类实现,以Push为例:
public class PushMessageReuqest extends MessageRequest { public PushMessageRequest() { super(MessageChannel.PUSH); } private String title; // 省略其他字段以及getter、setter方法 ...}我在接口入参使用MessageRequest接收:
public class MessageController { @PostMapping("/sendMessage") public R<Object> sendMessage(MessageRequest request) { System.out.println(request); }}使用postman发送push请求之后发现后端收到的类型还是基类,并且title字段丢失。


这与我预想的不符,因为客户端知道渠道,构建对应的渠道消息体给我就好了啊!为什么类型被擦除了呢?我的想法就是发送push请求啊。。。。。后来才知道序列化之后在反序列化的时候不知道给你反序列化成什么类型,序列化工具也没有聪明到能根据你的channel属性就知道是什么类型,但是我又想这样做。那么怎么办呢????
经过查询资料以及咨询了一下领导,发现了@JsonTypeInfo和@JsonSubTypes两个注解。
@JsonTypeInfo作用于类/接口,被用来开启多态类型处理,它有一些属性:
NONE:不使用识别码CLASS:使用完全限定类名做识别码MINIMAL_CLASS:使用类名(忽略包名)做识别码,和基类在同一个包可用NAME:指定名称CUSTOM:自定义识别码,由@JsonTypeIdResolver对应PROPERTY:作为兄弟属性加入,默认值WRAPPER_OBJECT:作为一个包装的对象WRAPPER_ARRAY:作为包装的数组EXTERNAL_PROPERTY:作为扩展属性EXISTING_PROPERTY:作为已存在的属性(符合我的场景,用channel)use为CLASS(不指定默认为@class)、MINIMAL_CLASS(不指定默认为@c)、NAME(不指定默认为@type,include为PROPERTY、EXISTING_PROPERTY、EXTERNAL_PROPERTY时才有效。@JsonSubTypes作用于类/接口,用来列出给定类/接口的子类。一般配合@JsonTypeInfo使用
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "channel")@JsonSubTypes({ @JsonSubTypes.Type(value = PushMessageRequest.class, name = "PUSH"), @JsonSubTypes.Type(value = EmailMessageRequest.class, name = "EMAIL")})JsonSubTypes的值是一个@JsonSubTypes.Type[]数组,参数value表示类型,参数name表示@JsonTypeInfo注解中property属性的值,对比以上代码即:channel = "PUSH"或channel = "EMAIL"。name为可选值,不指定时需在子类提供JsonTypeName注解并指定value属性。
改造上面提供的MessageReuqest
// include默认为PROPERTY,这里可以不加@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "channel")@JsonSubTypes({ @JsonSubTypes.Type(value = PushMessageRequest.class, name = "PUSH"), @JsonSubTypes.Type(value = EmailMessageRequest.class, name = "EMAIL")})public class MessageRequest implements Serializable { protected MessageChannel channel; private MessageRequest(){} protected MessageRequest(MessageChannel channel){ this.channel = channel; } public MessageChannel getChannel() { return this.channel; }}
此时通过postman请求发现入参类型有了变化


include属性使用默认的PROPERTY时发现序列化之后的json会多出来一个属性,属性名对应的就是@JsonTypeInfo的property的值。虽然不影响使用,但是我看着很不舒服。基于我这种情况可以使用include=EXISTING_PROPERTY。

本文来自博客园,作者:我叫乐多你养我啊,转载请注明原文链接:https://www.cnblogs.com/lvbok/p/16330663.html