虽然项目使用了枚举类Enum,来替代原来的Constant常量类,但Java对象的入参仍然是Integer类型,顶多再加个 {@link } 说明它是对应哪个枚举。
但这仍然无法限制传入的值不会乱传。要是有人不写{@link } 或者{@link } 写错,那更死。
1、最开始使用常量类的写法:(我觉得这种写法最烦的一点就是,如果常量类定义了Integer类型,就没法使用switch case来做判断)
- import com.aa.docking.common.DataDictionary;
- import lombok.Data;
-
- @Data
- public class DataReq {
- /**
- * 模式类型 {@link DataDictionary.ManageMode}
- */
- private Integer modeType;
- }
-
- /**
- * 数据字典
- */
- public class DataDictionary {
- /**
- * 模式类型
- */
- public class ManageMode {
- /**
- * 模式一
- */
- public static final Integer MODE_1 = 1;
- /**
- * 模式二
- */
- public static final Integer MODE_2 = 2;
- }
- }
2、改进以后使用枚举的写法:
- import lombok.Data;
- import com.aa.docking.module.amc.enums.outside.ManagementModeEnum;
-
- @Data
- public class DataReq {
- /**
- * 模式类型 {@link ManagementModeEnum}
- */
- private Integer modeType;
- }
-
-
- import lombok.AllArgsConstructor;
- import lombok.Getter;
- import lombok.experimental.Accessors;
- @Getter
- @Accessors(fluent = true)
- @AllArgsConstructor
- public enum ManagementModeEnum {
- MODE_1(1, "模式一"),
- MODE_2(2, "模式二"),
-
- ;
-
- /**
- * 字典码值
- */
- private Integer code;
- /**
- * 字典描述
- */
- private String desc;
-
- }
3、改进以后使用枚举代替Integer类型的写法:
- import com.aa.docking.module.amc.enums.outside.ManagementModeEnum;
- import lombok.Data;
-
- @Data
- public class DataReq {
- /**
- * 模式类型
- */
- private ManagementModeEnum modeType;
- }
但存在一个问题,传参的时候需要传入一个枚举对象,否则匹配不到:
- {
- "modeType": {
- "code": 1
- }
- }
我想要像原来一样直接传Integer类型就能匹配到枚举类:
- {
- "modeType": 1
- }
要怎么实现呢?继续往下看:
1)序列化过程:把一个 Java 对象变成二进制内容(byte[]数组)存储起来。这里的存储方式可能是存储到磁盘中,也可能是发布到网络中。
一个 Java 对象要能序列化,必须实现一个特殊的java.io.Serializable接口。
Serializable 没有定义任何方法,它是一个空接口。这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。
- package java.io;
-
- public interface Serializable {
- }
2) 反序列化过程:把一个二进制内容(byte[]数组)变回 Java 对象。
有了反序列化,保存到磁盘中的 byte[] 又可以“变回” Java 对象,或者从网络上读取 byte[] 并把它“变回” Java 对象。
1、首先定义接口IDictEnum(所有枚举类都要去实现该接口)
指定反序列化的方法:EnumJsonDeserializer
指定序列化的方法:EnumJsonSerializer
- import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
- import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
- /**
- * @description: 数据字典公共接口
- */
- @JsonDeserialize(using = EnumJsonDeSerializer.class)
- @JsonSerialize(using = EnumJsonSerializer.class)
- public interface IDictEnum {
- /**
- * 获得字典码值
- */
- Integer code();
-
- /**
- * 获得字典描述, 这里不能用name, 因为java.lang.Enum已经定义了name
- */
- String desc();
-
- }
2、反序列化方法:EnumJsonDeserializer
- import com.fasterxml.jackson.core.JsonParser;
- import com.fasterxml.jackson.core.JsonStreamContext;
- import com.fasterxml.jackson.databind.*;
- import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
-
- import java.io.IOException;
- import java.util.Arrays;
-
- /**
- * @description: 数据字典枚举反序列化
- * @see springboot项目中枚举类型的最佳实践
- */
- public class EnumJsonDeSerializer extends JsonDeserializer
implements ContextualDeserializer { -
- private Class extends IDictEnum> clazz;
-
- public EnumJsonDeSerializer() {
- }
-
- public EnumJsonDeSerializer(Class extends IDictEnum> clazz) {
- this.clazz = clazz;
- }
-
- @Override
- public IDictEnum deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException {
- String param = jsonParser.getText();
- IDictEnum[] enumConstants = clazz.getEnumConstants();
- JsonStreamContext parsingContext = jsonParser.getParsingContext();
- IDictEnum iDictEnum = Arrays.stream(enumConstants)
- .filter(x -> {
- //x.toString(),取枚举的具体值,如:xxx.enums.share.DelFlagEnum 枚举里的“NOT_DELETE”
- //从而使得两种形式都能识别
- String enumCodeStr = x.toString();
- return enumCodeStr.equals(param) || param.equals(x.code() + "");
- })
- .findFirst()
- .orElse(null);
- /*if (null == iEnum) {
- String msg = String.format("枚举类型%s从%s未能转换成功", clazz.toString(), param);
- throw new Exception(msg);
- }*/
- return iDictEnum;
- }
-
- @Override
- public Class> handledType() {
- return IDictEnum.class;
- }
-
- @SuppressWarnings({"unchecked"})
- @Override
- public JsonDeserializer> createContextual(DeserializationContext ctxt, BeanProperty property)
- throws JsonMappingException {
- JavaType type = property.getType();
- // 如果是容器,则返回容器内部枚举类型
- while (type.isContainerType()) {
- type = type.getContentType();
- }
- return new EnumJsonDeSerializer((Class extends IDictEnum>) type.getRawClass());
- }
- }
3、序列化方法:EnumJsonSerializer
- import com.fasterxml.jackson.core.JsonGenerator;
- import com.fasterxml.jackson.databind.JsonSerializer;
- import com.fasterxml.jackson.databind.SerializerProvider;
-
- import java.io.IOException;
-
- /**
- * @description: 数据字典枚举序列化
- * @see springboot项目中枚举类型的最佳实践
- */
- public class EnumJsonSerializer extends JsonSerializer
{ -
- @Override
- public void serialize(IDictEnum iDictEnum, JsonGenerator generator, SerializerProvider provider) throws IOException {
- // 序列化只要code的值
- generator.writeNumber(iDictEnum.code());
- // 序列化形式: {"code": "", "desc": ""}
- //generator.writeStartObject();
- //generator.writeNumberField("code", iBaseDict.code());
- //generator.writeStringField("desc", iBaseDict.desc());
- //generator.writeEndObject();
- }
-
- @Override
- public Class handledType() {
- return IDictEnum.class;
- }
- }
4、我们前面创建的枚举类,需要去实现 IDictEnum接口
- import com.aa.docking.base.common.enums.IDictEnum;
- import lombok.AllArgsConstructor;
- import lombok.Getter;
- import lombok.experimental.Accessors;
-
- @Getter
- @Accessors(fluent = true)
- @AllArgsConstructor
- public enum ManagementModeEnum implements IDictEnum {
- MODE_1(1, "模式一"),
- MODE_2(2, "模式二"),
-
- ;
-
- /**
- * 字典码值
- */
- private Integer code;
- /**
- * 字典描述
- */
- private String desc;
-
- }
5、这样就搞定啦!!经验证,还支持List,Map等复杂的传参形式哦!!
- @Data
- public class NeighInfoPageReq {
- private ManagementModeEnum modeType;
- private List
modeTypeList; - private Map
map1; - }
传参只要带Integer类型就能匹配到枚举类啦^_^
- {
- "modeType": 1,
- "modeTypeList": [1, 2]
- "map1": {
- "模式一", 1,
- "模式二", 2
- }
- }
6、假如枚举值传错了要怎么判断呢?加上validation的@NotNull就可以啦。此时如果传“managementMode”: 3 就会报错。
- import lombok.Data;
- import javax.validation.constraints.NotNull;
-
- @Data
- public class NeighInfoPageReq {
- /**
- * 模式类型
- */
- @NotNull(message = "模式不能为空或码值不正确")
- private ManagementModeEnum modeType;
- }
1、mybatis有自己的一套序列化反序列化规则,所以我们还需要使用@MappedTypes,单独针对IDictEnum定义一套规则。
- package com.aa.docking.base.common.enums;
-
- import com.aa.docking.base.common.util.EnumUtil;
- import org.apache.ibatis.type.BaseTypeHandler;
- import org.apache.ibatis.type.JdbcType;
- import org.apache.ibatis.type.MappedTypes;
-
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
-
- /**
- * @description: mybatis自定义枚举类型转换
- */
- @MappedTypes({IDictEnum.class})
- public class EnumTypeHandler
extends Enum> & IDictEnum> extends BaseTypeHandler { - private Class
type; -
- public EnumTypeHandler(Class
type) { - if (type == null) {
- throw new IllegalArgumentException("Type argument cannot be null.");
- }
- this.type = type;
- }
-
- /**
- * 用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型
- */
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, IDictEnum parameter, JdbcType jdbcType)
- throws SQLException {
- ps.setInt(i, parameter.code());
- }
-
- /**
- * 用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型
- */
- @Override
- public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
- int code = rs.getInt(columnName);
- return rs.wasNull() ? null : codeOf(code);
- }
-
- /**
- * 用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型
- */
- @Override
- public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- int code = rs.getInt(columnIndex);
- return rs.wasNull() ? null : codeOf(code);
- }
-
- /**
- * 用定义调用存储过程后,如何把数据库类型转换为对应的Java类型
- */
- @Override
- public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- int code = cs.getInt(columnIndex);
- return cs.wasNull() ? null : codeOf(code);
- }
-
- private E codeOf(int code) {
- try {
- return EnumUtil.getEnumByCode(type, code);
- } catch (Exception ex) {
- throw new IllegalArgumentException("Cannot convert " + code + " to " + type.getSimpleName() + " by code value.", ex);
- }
- }
- }
这里的EnumUtil:
- import com.aa.docking.base.common.enums.IDictEnum;
- import org.apache.commons.lang3.StringUtils;
-
- /**
- * @description: 公共枚举方法
- * @see 枚举Enum使用范例-公共枚举方法
- */
- public class EnumUtil {
-
- /**
- * 根据code获取枚举
- */
- public static
extends IDictEnum> T getEnumByCode(Class tClass, Integer code) { - if (code != null) {
- for (T t : tClass.getEnumConstants()) {
- if (t.code().equals(code)) {
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * 根据desc获取枚举
- */
- public static
extends IDictEnum> T getEnumByDesc(Class tClass, String desc) { - if (StringUtils.isNotBlank(desc)) {
- for (T t : tClass.getEnumConstants()) {
- if (t.desc().equals(desc)) {
- return t;
- }
- }
- }
- return null;
- }
-
- /**
- * 根据code获取desc
- */
- public static
extends IDictEnum> String getDescByCode(Class tClass, Integer code) { - T t = getEnumByCode(tClass, code);
- if (null != t) {
- return t.desc();
- }
- return null;
- }
-
- /**
- * 根据desc获取code
- */
- public static
extends IDictEnum> Integer getCodeByDesc(Class tClass, String desc) { - T t = getEnumByDesc(tClass, desc);
- if (null != t) {
- return t.code();
- }
- return null;
- }
- }
2、配置文件里面还需要定义mybatis的typeHandler扫描包路径:
- # mybatis配置参数
- mybatis:
- # 定义typeHandler扫描包路径
- type-handlers-package: com.aa.docking
是不是很棒*_^
