一文詳解Java項(xiàng)目中如何優(yōu)雅的使用枚舉類型
前言
枚舉類型在開發(fā)中是很常見的,有非常多的應(yīng)用場景,如狀態(tài)管理、類型分類、權(quán)限控制、配置管理、錯(cuò)誤碼管理、日志級(jí)別等。正確合理的使用枚舉可以給我們帶來非常多的好處:
- 增強(qiáng)代碼可讀性:枚舉可以使得代碼更加清晰、易于理解。它們提供了一種方式來組織和表示相關(guān)的常量值,使得代碼更易于閱讀和維護(hù)。
- 類型安全性:枚舉類型能夠限制變量的值,只能取枚舉類型中定義的常量之一,從而避免了錯(cuò)誤的賦值。這有助于減少代碼中的錯(cuò)誤,并提高代碼的穩(wěn)定性。
- 更好的維護(hù)性:枚舉類型可以在編譯時(shí)進(jìn)行類型檢查,這有助于更早地發(fā)現(xiàn)和修復(fù)問題。此外,由于枚舉類型中的常量值是預(yù)定義的,因此可以減少對(duì)常量值的修改,從而簡化代碼的維護(hù)。
- 更好的性能:枚舉類型的值是在編譯時(shí)確定的,因此在運(yùn)行時(shí)訪問枚舉類型的值會(huì)更快。此外,由于枚舉類型中的常量值是唯一的,因此可以直接使用“==”進(jìn)行兩個(gè)值之間的對(duì)比,這有助于提高性能。
- 更好的組織性:枚舉類型可以幫助我們將相關(guān)的值組織在一起,使代碼更加整潔。通過將相關(guān)的常量值組合在一起,可以使代碼更加易于理解和維護(hù)。
- 可擴(kuò)展性:枚舉類型可以輕松地?cái)U(kuò)展或更新,而不會(huì)對(duì)其他部分的代碼造成影響。這有助于保持代碼的靈活性和可擴(kuò)展性。
- 便于測(cè)試:枚舉類型可以方便地進(jìn)行測(cè)試,因?yàn)樗鼈兙哂杏邢耷掖_定的值域。這使得測(cè)試人員可以更容易地覆蓋所有可能的場景,并確保代碼的正確性。
雖然枚舉有諸多的好處,但是使用枚舉也給我們帶來了一些困擾:
- 前后端數(shù)據(jù)格式轉(zhuǎn)換:前端主要給用戶展示數(shù)據(jù),不能直接顯示枚舉值,需要前端將枚舉轉(zhuǎn)成用戶可讀的數(shù)據(jù)顯示
- 數(shù)據(jù)庫的存儲(chǔ):代碼中的枚舉類型無法直接存儲(chǔ)數(shù)據(jù)庫,一般轉(zhuǎn)成數(shù)值類型,這樣還可以減少存儲(chǔ)空間
- 代碼中大量類型轉(zhuǎn)換:查詢時(shí)需要數(shù)值類型轉(zhuǎn)成枚舉類型,保存時(shí)又需要將枚舉類型轉(zhuǎn)成數(shù)值類型
針對(duì)枚舉存在的問題,本文介紹一種枚舉從數(shù)據(jù)庫-->后端代碼-->前端代碼-->頁面和從頁面-->前端代碼-->后端代碼-->數(shù)據(jù)庫的自動(dòng)轉(zhuǎn)換方案,大大方便前后端使用枚舉類型。
自動(dòng)轉(zhuǎn)換目標(biāo)
我們以用戶狀態(tài)為例,用戶有兩種狀態(tài):禁用和啟用
前端頁面:前端頁面顯示用戶狀態(tài)時(shí)用“禁用、啟用”;
前端代碼:前端代碼里處理用戶狀態(tài)時(shí)用:“ENABLE、DISABLE”或者用“0、1”;
后端代碼:后端代碼使用StatusEnum枚舉類;
數(shù)據(jù)庫:數(shù)據(jù)庫存儲(chǔ)用戶狀態(tài)時(shí)禁用存1、啟用存0。
我們的目標(biāo)是讓枚舉在各個(gè)環(huán)境流轉(zhuǎn)時(shí)全自動(dòng)轉(zhuǎn)換。
代碼與數(shù)據(jù)庫自動(dòng)轉(zhuǎn)換
第一步創(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
實(shí)現(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
主要有三個(gè)方法
getCode()
獲取枚舉的數(shù)值如“0、1”;getName()
獲取枚舉顯示值如“禁用、啟用” ;getEnumName()
獲取枚舉的枚舉值如“ENABLE、DISABLE”.
如果使用MybatisPlus, 可以使用@EnumValue
注解很方便的幫我們解決數(shù)據(jù)庫與實(shí)體對(duì)象中枚舉類型的相互轉(zhuǎn)換,如果只使用的Mybatis可以自定義TypeHandler
來解決數(shù)據(jù)庫到JAVA枚舉對(duì)象的自動(dòng)轉(zhuǎn)換。
第三步創(chuàng)建用戶類User
用戶狀態(tài)使用StatusEnum
類
@Data @TableName("user") public class User { private Long id; private String userName; private StatusEnum status; }
前后端相互轉(zhuǎn)換
當(dāng)前端查詢用戶時(shí),我們希望將枚舉的三個(gè)屬性都返回給前端,前端頁面顯示時(shí)取status.name
代碼中使用status.enum
或者status.code
{ "id": 3581209395268, "userName": "test2@8531.cn", "status": { "name": "禁用", "enum": "DISABLE", "code": 1 } }
為了達(dá)到將枚舉序列化成一個(gè)json對(duì)象,我們需要自定義序列化器和反序列化器,以下以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
格式的請(qǐng)求,當(dāng)請(qǐng)求類型為application/x-www-form-urlencoded
我們還需要自定義Spring消息轉(zhuǎn)換器
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); } } }
以上兩個(gè)消息轉(zhuǎn)換器可以在數(shù)據(jù)格式以表單形式提交時(shí)將數(shù)值類型(0、1)和枚舉值類型(ENABLE、DISABLE)轉(zhuǎn)成枚舉類型。
將自定義好的數(shù)據(jù)轉(zhuǎn)換器注入到Spring中,這樣就完成所有枚舉自動(dòng)轉(zhuǎn)換。
@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
對(duì)應(yīng)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; } }
這樣很方便的解決了枚舉在各個(gè)環(huán)節(jié)的自動(dòng)轉(zhuǎn)換問題,其它枚舉只要實(shí)現(xiàn)BaseEnum
接口就能實(shí)現(xiàn)全自動(dòng)轉(zhuǎn)換,前后端用起來也方便了不少。
總結(jié)
本文主要介紹了項(xiàng)目中使用枚舉的優(yōu)缺點(diǎn),并針對(duì)缺點(diǎn)給出了解決方案,解決了枚舉在項(xiàng)目中頻繁轉(zhuǎn)換的問題,當(dāng)然解決的還不是非常完美,比如返回給前端的枚舉格式是:{"enum":"DISABLE","code":1}
但是保存時(shí)傳此數(shù)據(jù)結(jié)構(gòu),后端卻無法正確的轉(zhuǎn)成枚舉,我們可以創(chuàng)建StatusEnumDeserializer
,將子json對(duì)象轉(zhuǎn)成對(duì)應(yīng)枚舉就好了,但是范型的寫法目前還不知道怎么寫,不可能增加一個(gè)枚舉寫一個(gè)反序列化器,有知道的可以回復(fù)一下,相互學(xué)習(xí)。
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項(xiàng)目中如何優(yōu)雅的使用枚舉類型的詳細(xì)內(nèi)容,更多關(guān)于Java枚舉類型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot整合mybatis/mybatis-plus實(shí)現(xiàn)數(shù)據(jù)持久化的操作
這篇文章主要介紹了SpringBoot整合mybatis/mybatis-plus實(shí)現(xiàn)數(shù)據(jù)持久化,本節(jié)內(nèi)容我們介紹了數(shù)據(jù)持久化的相關(guān)操作,并且是基礎(chǔ)傳統(tǒng)的關(guān)系型數(shù)據(jù)庫——mysql,需要的朋友可以參考下2022-10-10SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能
這篇文章主要介紹了SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03SpringMVC中的HandlerMapping和HandlerAdapter詳解
這篇文章主要介紹了SpringMVC中的HandlerMapping和HandlerAdapter詳解,在Spring MVC中,HandlerMapping(處理器映射器)用于確定請(qǐng)求處理器對(duì)象,請(qǐng)求處理器可以是任何對(duì)象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下2023-08-08Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決
這篇文章主要介紹了Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08springboot項(xiàng)目配置context path失效的問題解決
本文主要介紹了springboot項(xiàng)目配置context path失效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04基于Hibernate中配置文件的學(xué)習(xí)(分享)
下面小編就為大家?guī)硪黄贖ibernate中配置文件的學(xué)習(xí)(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06