需求描述:
項目中可能有這麼一種情況,我們對外提供一個http
接口服務,但是這個接口需要接收某一類具有共同特徵的參數(父類 – 子類)。比如一個消息服務接口去提供發送郵件,短信,根據傳入的消息類型不同而調用不同的發送邏輯,即接口接收父類類型,傳入的參數需要反序列化爲實際的子類類型。
接口示例
/**
* 暴露http接口,供feign調用
*
* @author [email protected]
* @date 2019/9/10
*/
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/api/v1/send")
public void send(@RequestBody MessageContext messageContext) {
rabbitTemplate.convertAndSend("t_message", messageContext);
}
}
類圖結構
接口接收一個父類類型,但是實際上傳入的類型爲子類類型。
根據類型反序列化(多態支持)
父類
/**
* 消息體父類
*
* @author [email protected]
* @date 2019/9/10
*/
@Data
// 根據字段值進行反序列化態判斷,字段 MessageContext.messageType,並在子類中攜帶此字段值
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "messageType", visible = true)
@JsonSubTypes({
// 值-->子類映射
@JsonSubTypes.Type(value = SimpleEmailContext.class, name = MessageType.StringType.SIMPLE_EMAIL),
@JsonSubTypes.Type(value = SecretEmailContext.class, name = MessageType.StringType.SECRET_EMAIL),
@JsonSubTypes.Type(value = SMSContext.class, name = MessageType.StringType.SMS)
})
public class MessageContext {
/**
* 消息類型
*/
protected MessageType messageType;
/**
* 消息發送者
*/
protected String from;
/**
* 消息接受者
*/
protected List<String> to;
/**
* 消息內容
*/
protected String content;
/**
* 消息ID
*/
private String messageId = UUID.randomUUID().toString();
public MessageContext(MessageType messageType, String from, List<String> to, String content) {
this.messageType = messageType;
this.from = from;
this.to = to;
this.content = content;
}
}
注:在父類配置反序列化規則,此配置雖然可用,但是如果子類非常多,此方式就不太優美了,並且這樣做也違反了開閉原則。
子類
/**
* 簡單郵件消息體
*
* @author [email protected]
* @date 2019/9/10
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SimpleEmailContext extends MessageContext {
private String subject;
private List<String> cc;
private List<String> bcc;
@Builder
public SimpleEmailContext(String from, List<String> to, String subject, String content, List<String> cc, List<String> bcc) {
super(MessageType.SIMPLE_EMAIL, from, to, content);
this.subject = subject;
this.cc = cc;
this.bcc = bcc;
}
}
不違反開閉原則的方式
之前看過有貌似另一種方式可以只新增不修改,不違反開閉原則,但沒有深究,有興趣的話可自行Google。
示例代碼
可以在 https://gitee.com/tangjizhou/message 查看完整版本代碼。