一文詳解Java項目中如何優(yōu)雅的使用枚舉類型
前言
枚舉類型在開發(fā)中是很常見的,有非常多的應用場景,如狀態(tài)管理、類型分類、權限控制、配置管理、錯誤碼管理、日志級別等。正確合理的使用枚舉可以給我們帶來非常多的好處:
- 增強代碼可讀性:枚舉可以使得代碼更加清晰、易于理解。它們提供了一種方式來組織和表示相關的常量值,使得代碼更易于閱讀和維護。
- 類型安全性:枚舉類型能夠限制變量的值,只能取枚舉類型中定義的常量之一,從而避免了錯誤的賦值。這有助于減少代碼中的錯誤,并提高代碼的穩(wěn)定性。
- 更好的維護性:枚舉類型可以在編譯時進行類型檢查,這有助于更早地發(fā)現(xiàn)和修復問題。此外,由于枚舉類型中的常量值是預定義的,因此可以減少對常量值的修改,從而簡化代碼的維護。
- 更好的性能:枚舉類型的值是在編譯時確定的,因此在運行時訪問枚舉類型的值會更快。此外,由于枚舉類型中的常量值是唯一的,因此可以直接使用“==”進行兩個值之間的對比,這有助于提高性能。
- 更好的組織性:枚舉類型可以幫助我們將相關的值組織在一起,使代碼更加整潔。通過將相關的常量值組合在一起,可以使代碼更加易于理解和維護。
- 可擴展性:枚舉類型可以輕松地擴展或更新,而不會對其他部分的代碼造成影響。這有助于保持代碼的靈活性和可擴展性。
- 便于測試:枚舉類型可以方便地進行測試,因為它們具有有限且確定的值域。這使得測試人員可以更容易地覆蓋所有可能的場景,并確保代碼的正確性。
雖然枚舉有諸多的好處,但是使用枚舉也給我們帶來了一些困擾:
- 前后端數(shù)據(jù)格式轉換:前端主要給用戶展示數(shù)據(jù),不能直接顯示枚舉值,需要前端將枚舉轉成用戶可讀的數(shù)據(jù)顯示
- 數(shù)據(jù)庫的存儲:代碼中的枚舉類型無法直接存儲數(shù)據(jù)庫,一般轉成數(shù)值類型,這樣還可以減少存儲空間
- 代碼中大量類型轉換:查詢時需要數(shù)值類型轉成枚舉類型,保存時又需要將枚舉類型轉成數(shù)值類型
針對枚舉存在的問題,本文介紹一種枚舉從數(shù)據(jù)庫-->后端代碼-->前端代碼-->頁面和從頁面-->前端代碼-->后端代碼-->數(shù)據(jù)庫的自動轉換方案,大大方便前后端使用枚舉類型。
自動轉換目標
我們以用戶狀態(tài)為例,用戶有兩種狀態(tài):禁用和啟用
前端頁面:前端頁面顯示用戶狀態(tài)時用“禁用、啟用”;
前端代碼:前端代碼里處理用戶狀態(tài)時用:“ENABLE、DISABLE”或者用“0、1”;
后端代碼:后端代碼使用StatusEnum枚舉類;
數(shù)據(jù)庫:數(shù)據(jù)庫存儲用戶狀態(tài)時禁用存1、啟用存0。
我們的目標是讓枚舉在各個環(huán)境流轉時全自動轉換。
代碼與數(shù)據(jù)庫自動轉換
第一步創(chuàng)建統(tǒng)一的枚舉基類BaseEnum
public interface BaseEnum { int getCode(); String getName(); String getEnumName(); static <T extends BaseEnum> T getInstance(Class<T> clazz, String value) { T[] constants = clazz.getEnumConstants(); for (T t : constants) { if(StrUtil.isNumeric(value)){ if (t.getCode() == Integer.parseInt(value)) { return t; } }else { if (t.getEnumName().equals(value)) { return t; } } } return null; } }
第二步創(chuàng)建用戶狀態(tài)類StatusEnum
實現(xiàn)BaseEnum
接口
public enum StatusEnum implements BaseEnum { ENABLE(0,"啟用"), DISABLE(1,"禁用"); @EnumValue private int code; private String name; StatusEnum(int code, String name) { this.code = code; this.name=name; } @Override public int getCode() { return code; } @Override public String getName() { return name; } @Override public String getEnumName() { return this.name(); } }
BaseEnum
主要有三個方法
getCode()
獲取枚舉的數(shù)值如“0、1”;getName()
獲取枚舉顯示值如“禁用、啟用” ;getEnumName()
獲取枚舉的枚舉值如“ENABLE、DISABLE”.
如果使用MybatisPlus, 可以使用@EnumValue
注解很方便的幫我們解決數(shù)據(jù)庫與實體對象中枚舉類型的相互轉換,如果只使用的Mybatis可以自定義TypeHandler
來解決數(shù)據(jù)庫到JAVA枚舉對象的自動轉換。
第三步創(chuàng)建用戶類User
用戶狀態(tài)使用StatusEnum
類
@Data @TableName("user") public class User { private Long id; private String userName; private StatusEnum status; }
前后端相互轉換
當前端查詢用戶時,我們希望將枚舉的三個屬性都返回給前端,前端頁面顯示時取status.name
代碼中使用status.enum
或者status.code
{ "id": 3581209395268, "userName": "test2@8531.cn", "status": { "name": "禁用", "enum": "DISABLE", "code": 1 } }
為了達到將枚舉序列化成一個json對象,我們需要自定義序列化器和反序列化器,以下以SpringBoot自帶的Jackson為例:
public class BaseEnumSerializer extends StdSerializer<BaseEnum> { public BaseEnumSerializer() { this(null); } public BaseEnumSerializer(Class<BaseEnum> t) { super(t); } @Override public void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name",value.getName()); gen.writeStringField("enum",value.getEnumName()); gen.writeNumberField("code",value.getCode()); gen.writeEndObject();; } } public class BaseEnumDeserializer<T extends BaseEnum> extends StdDeserializer<T> { private Class<T> type; public BaseEnumDeserializer() { this(null); } public BaseEnumDeserializer(Class<T> vc) { super(vc); type = vc; } @Override public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { return BaseEnum.getInstance(type, p.getText()); } }
自定義Jackson序列化器與反序列化器只能解決數(shù)據(jù)類型為application/json
格式的請求,當請求類型為application/x-www-form-urlencoded
我們還需要自定義Spring消息轉換器
public class NumBaseEnumConverterFactory implements ConverterFactory<Number, BaseEnum> { @Override public <T extends BaseEnum> Converter<Number, T> getConverter(Class<T> aClass) { return new NumberToEnumConverter<>(aClass); } private final class NumberToEnumConverter<T extends BaseEnum> implements Converter<Number, T> { private Class<T> enumType; public NumberToEnumConverter(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(Number s) { return BaseEnum.getInstance(enumType,s.toString()); } } } public class StrBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> { @Override public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> aClass) { return new StringToEnumConverter<>(aClass); } private final class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> { private Class<T> enumType; public StringToEnumConverter(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(String s) { return BaseEnum.getInstance(enumType,s); } } }
以上兩個消息轉換器可以在數(shù)據(jù)格式以表單形式提交時將數(shù)值類型(0、1)和枚舉值類型(ENABLE、DISABLE)轉成枚舉類型。
將自定義好的數(shù)據(jù)轉換器注入到Spring中,這樣就完成所有枚舉自動轉換。
@Configuration public class WebConfig implements WebMvcConfigurer { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(BaseEnum.class, new BaseEnumSerializer()); module.addDeserializer(BaseEnum.class, new BaseEnumDeserializer<>()); mapper.registerModule(module); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); return mapper; } @Override public void addFormatters(FormatterRegistry registry) { registry.addConverterFactory(new StrBaseEnumConverterFactory()); registry.addConverterFactory(new NumBaseEnumConverterFactory()); } }
查詢用戶
GET http://localhost:90/user/3581209395268 返回: { "id": 3581209395268, "userName": "test2@8531.cn", "status": { "name": "啟用", "enum": "ENABLE", "code": 0 } }
application/json格式傳參
POST http://localhost:90/user Content-Type: application/json { "id": 3581209395268, "status": "DISABLE" } ### POST http://localhost:90/user Content-Type: application/json { "id": 3581209395268, "status": "0" }
application/x-www-form-urlencoded格式傳參
PUT http://localhost:90/user
Content-Type: application/x-www-form-urlencoded
id=3581209395268&status=ENABLE
###
PUT http://localhost:90/user
Content-Type: application/x-www-form-urlencoded
id=3581209395268&status=1
###
PUT http://localhost:90/user/3581209395268?status=ENABLE
Content-Type: application/x-www-form-urlencoded
###
PUT http://localhost:90/user/3581209395268?status=1
Content-Type: application/x-www-form-urlencoded
@PathVariable格式傳參
PUT http://localhost:90/user/3581209395268/ENABLE
Content-Type: application/x-www-form-urlencoded
###
PUT http://localhost:90/user/3581209395268/1
Content-Type: application/x-www-form-urlencoded
對應JAVA代碼:
@RestController public class UserController { @Resource private UserMapper userMapper; @GetMapping("/user/{id}") public User getById(@PathVariable Long id) { return userMapper.selectById(id); } @PostMapping("/user") public User upadteById(@RequestBody User user) { userMapper.updateById(user); return user; } @PutMapping("/user") public User updateUser(User user) { userMapper.updateById(user); return user; } @PutMapping("/user/{id}/{status}") public User updateStatus(@PathVariable Long id,@PathVariable StatusEnum status) { User user=userMapper.selectById(id); user.setStatus(status); userMapper.updateById(user); return user; } @PutMapping("/user/{id}") public User updateUserStatus(@PathVariable Long id,@RequestParam StatusEnum status) { User user=userMapper.selectById(id); user.setStatus(status); userMapper.updateById(user); return user; } }
這樣很方便的解決了枚舉在各個環(huán)節(jié)的自動轉換問題,其它枚舉只要實現(xiàn)BaseEnum
接口就能實現(xiàn)全自動轉換,前后端用起來也方便了不少。
總結
本文主要介紹了項目中使用枚舉的優(yōu)缺點,并針對缺點給出了解決方案,解決了枚舉在項目中頻繁轉換的問題,當然解決的還不是非常完美,比如返回給前端的枚舉格式是:{"enum":"DISABLE","code":1}
但是保存時傳此數(shù)據(jù)結構,后端卻無法正確的轉成枚舉,我們可以創(chuàng)建StatusEnumDeserializer
,將子json對象轉成對應枚舉就好了,但是范型的寫法目前還不知道怎么寫,不可能增加一個枚舉寫一個反序列化器,有知道的可以回復一下,相互學習。
public class StatusEnumDeserializer extends JsonDeserializer<StatusEnum> { @Override public StatusEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node= p.getCodec().readTree(p); if(node.isObject()){ String name= node.get("enum").toString(); return BaseEnum.getInstance(StatusEnum.class, name); }else { return BaseEnum.getInstance(StatusEnum.class, node.textValue()); } } }
以上就是一文詳解Java項目中如何優(yōu)雅的使用枚舉類型的詳細內容,更多關于Java枚舉類型的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot整合mybatis/mybatis-plus實現(xiàn)數(shù)據(jù)持久化的操作
這篇文章主要介紹了SpringBoot整合mybatis/mybatis-plus實現(xiàn)數(shù)據(jù)持久化,本節(jié)內容我們介紹了數(shù)據(jù)持久化的相關操作,并且是基礎傳統(tǒng)的關系型數(shù)據(jù)庫——mysql,需要的朋友可以參考下2022-10-10SpringBoot+Vue實現(xiàn)數(shù)據(jù)添加功能
這篇文章主要介紹了SpringBoot+Vue實現(xiàn)數(shù)據(jù)添加功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03SpringMVC中的HandlerMapping和HandlerAdapter詳解
這篇文章主要介紹了SpringMVC中的HandlerMapping和HandlerAdapter詳解,在Spring MVC中,HandlerMapping(處理器映射器)用于確定請求處理器對象,請求處理器可以是任何對象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下2023-08-08Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決
這篇文章主要介紹了Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08springboot項目配置context path失效的問題解決
本文主要介紹了springboot項目配置context path失效的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04