这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战
上篇文章《Java反射之枚举类型》,讲解了枚举的基本用法。今天介绍枚举在项目中的实际使用,包括数据持久层、序列化的使用。
枚举普通用法
先有数据库表order
CREATE TABLE `order` (
`id` int NOT NULL AUTO_INCREMENT,
`pay_status` int DEFAULT NULL COMMENT '支付状态,1:未付款;2:已付款;-1:已取消',
`pay_type` int DEFAULT NULL COMMENT '支付方式,1:微信支付;2:支付宝支付',
# 其他字段忽略
PRIMARY KEY (`id`)
)
对应实体类
public class Order {
private Long id;
/**
* 支付状态,1:未付款;2:已付款;-1:已取消
*
*/
private Integer payStatus;
/**
* 支付方式,1:微信支付;2:支付宝支付
*
*/
private Integer payType;
// 其他字段忽略
这两个字段:pay_status、pay_type的值可以用枚举表示,如下:
public enum PayStatus {
WAITING_APY(1, "待支付"),
FINISH_PAY(2, "支付完成"),
CANCEL(-1, "已取消"),
;
@EnumValue
private Integer code;
private String msg;
PayStatus(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
public enum PayStatus {
WAITING_PAY(1, "待支付"),
FINISH_PAY(2, "支付完成"),
RETURN(3, "已退回"),
;
@EnumValue
private Integer code;
private String msg;
PayStatus(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static PayStatus getByCode(Integer code) {
for (PayStatus payStatus : PayStatus.values()) {
if (Objects.equals(payStatus.getCode(), code)) {
return payStatus;
}
}
return null;
}
}
使用枚举
public void createOrder() {
Order newOrder = new Order();
newOrder.setPayStatus(PayStatus.WAITING_PAY.getCode());
newOrder.setPayType(PayType.ALI_PAY.getCode());
}
public void handleOrder() {
Order order = orderSerivce.getById(1);
Integer payStatusCode = order.getPayStatus();
PayStatus payStatus = PayStatus.getByCode(payStatusCode);
if (payStatus == PayStatus.RETURN) {
//业务枚举
}
}
数据持久层直接使用枚举
像上面的使用枚举方式,有个麻烦的点:需要手动获取枚举值的code,在set到对象中;需要获取枚举,需要调用getByCode()方法。
在Mybatis Plus框架中,可以直接将对象属性定义为枚举类型。
public class Order {
private Long id;
/**
* 支付状态,1:未付款;2:已付款;-1:已取消
*
*/
private PayStatus payStatus;
/**
* 支付方式,1:微信支付;2:支付宝支付
*
*/
private PayType payType;
// 其他字段忽略
只需要在与数据表字段对应的枚举属性加上@EnumValue
public enum PayStatus {
WAITING_PAY(1, "待支付"),
FINISH_PAY(2, "支付完成"),
RETURN(3, "已退回"),
;
@EnumValue
private Integer code;
private String msg;
PayStatus(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static PayStatus getByCode(Integer code) {
for (PayStatus payStatus : PayStatus.values()) {
if (Objects.equals(payStatus.getCode(), code)) {
return payStatus;
}
}
return null;
}
}
详细可参考官方文档:mp.baomidou.com/guide/enum.…
这样,我们就可以使用枚举进行业务逻辑处理
public void createOrder() {
Order newOrder = new Order();
newOrder.setPayStatus(PayStatus.WAITING_PAY);
newOrder.setPayType(PayType.ALI_PAY);
}
public void handleOrder() {
Order order = orderSerivce.getById(1);
PayStatus payStatus = order.getPayStatus();
if (payStatus == PayStatus.RETURN) {
//...
}
}
前后端交互序列化和反序列化
默认序列化方式
在Spring Boot的Web中,请求的参数和响应数据默认通过com.fasterxml.jackson序列化和反序列化。
枚举序列化成枚举的名称name(即定义的名称)。演示:
@GetMapping("order/get")
public Order getOrder() {
Order order = new Order();
order.setId(1);
order.setPayStatus(PayStatus.WAITING_PAY);
order.setPayType(PayType.WX_PAY);
return order;
}
枚举的反序列化有两种形式:枚举名称和顺序,即java.lang.Enum
类的两个属性name
和ordinal
演示:
@PostMapping("/order/search")
public List<Order> search(@RequestBody Order order) {
return null;
}
请求参数: {"id":1,"payType":0,"payStatus":"WAITING_PAY"}
从断点可以看出,payType=0反序列化为WX_PAY枚举,payStatus="WAITING_PAY"反序列化为WAITING_PAY枚举。
特别注意的一点,反序列化的值为整型时,其表示的是枚举的顺序,而不是枚举属性里的值。比如某个枚举只有2个枚举值(不管属性值是什么),反序列化的取值范围只有0,1,其他都会报错
自定义序列化方式
有时前端只需要我们返回枚举里的属性值,比如payType返回的1,2用来表示微信支付、支付宝支付。我们只需要在该属性值加上@JsonValue
注解
public enum PayType {
WX_PAY(1, "微信支付", 20),
ALI_PAY(2, "支付宝支付", 10);
@JsonValue
private Integer code;
private String msg;
private int sort;
PayType(Integer code, String msg, int sort) {
this.code = code;
this.msg = msg;
this.sort = sort;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public int getSort() {
return sort;
}
}
还可以通过实现抽象类JsonSerializer
和JsonDeserializer
序列化和反序列化。具体流程:
- 实现抽象类
JsonSerializer
和JsonDeserializer
- 在对应的字段加上注解
@JsonSerialize
和@JsonDeserialize
完整代码
序列化
package com.carrywei.breadspring.serializer;
import com.carrywei.breadspring.enums.PayStatus;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
*
* @version 1.0
* @date 2021/11/2
*/
@Component
public class PayStatusSerializer extends JsonSerializer {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
PayStatus payStatus = (PayStatus) o;
jsonGenerator.writeString(payStatus.getMsg());
}
}
反序列化
package com.carrywei.breadspring.serializer;
import com.carrywei.breadspring.enums.PayStatus;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
*
* @version 1.0
* @date 2021/11/2
*/
@Component
public class PayStatusDeserializer extends JsonDeserializer<PayStatus> {
@Override
public PayStatus deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
int intValue = jsonParser.getIntValue();
return PayStatus.getByCode(intValue);
}
}
加注解
/**
* 支付状态
*/
@JsonSerialize(using = PayStatusSerializer.class)
@JsonDeserialize(using = PayStatusDeserializer.class)
private PayStatus payStatus;
通用反序列化
对于枚举的反序列化,基本上所有的枚举都是一样的逻辑:将枚举属性值反序列化成枚举。为了确定枚举哪个属性值为反序列化用到的,我们定义接口BaseEnum,其中getCode()方法返回该属性值。
public interface BaseEnum<T> {
/**
* 获取枚举值
* @return
*/
T getCode();
/**
* 获取枚举值
* @return
*/
String getMsg();
}
此外,所有枚举实现该接口
public enum PayType implements BaseEnum<Integer> {}
public enum PayStatus implements BaseEnum<Integer> {}
在具体反序列化过程中,借助反射技术,实现枚举通用的反序列化方法。完整代码如下:
@Component
@Slf4j
public class BaseEnumJsonDeserializer extends JsonDeserializer {
private BucLogger logger = new BucLogger(BaseEnumJsonDeserializer.class);
@Override
public BaseEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
BaseEnum result = null;
try {
// 获取当前待反序列化的字段名称
String currentName = jsonParser.getCurrentName();
// 通过反射获取当前字段
Field declaredField = jsonParser.getCurrentValue().getClass().getDeclaredField(currentName);
// 当对象类型是BaseEnum类型的子类
if (BaseEnum.class.isAssignableFrom(declaredField.getType())) {
// 枚举值
Object[] enumConstants = declaredField.getType().getEnumConstants();
for (Object obj : enumConstants) {
if (String.valueOf(((BaseEnum) obj).getCode()).equals(jsonParser.getText())) {
result = (BaseEnum) obj;
break;
}
}
}
} catch (NoSuchFieldException e) {
logger.info("BaseEnumJsonDeserializer反序列化异常, obj = {}, currentName = {}", jsonParser.getCurrentValue(),
jsonParser.getCurrentName());
throw new BusinessException(CommonCodeMsg.SYSTEM_ERROR);
}
return result;
}
}