一文詳解Java項(xiàng)目中如何優(yōu)雅的使用枚舉類型
前言
枚舉類型在開(kāi)發(fā)中是很常見(jiàn)的,有非常多的應(yīng)用場(chǎng)景,如狀態(tài)管理、類型分類、權(quán)限控制、配置管理、錯(cuò)誤碼管理、日志級(jí)別等。正確合理的使用枚舉可以給我們帶來(lái)非常多的好處:
- 增強(qiáng)代碼可讀性:枚舉可以使得代碼更加清晰、易于理解。它們提供了一種方式來(lái)組織和表示相關(guān)的常量值,使得代碼更易于閱讀和維護(hù)。
- 類型安全性:枚舉類型能夠限制變量的值,只能取枚舉類型中定義的常量之一,從而避免了錯(cuò)誤的賦值。這有助于減少代碼中的錯(cuò)誤,并提高代碼的穩(wěn)定性。
- 更好的維護(hù)性:枚舉類型可以在編譯時(shí)進(jìn)行類型檢查,這有助于更早地發(fā)現(xiàn)和修復(fù)問(wèn)題。此外,由于枚舉類型中的常量值是預(yù)定義的,因此可以減少對(duì)常量值的修改,從而簡(jiǎn)化代碼的維護(hù)。
- 更好的性能:枚舉類型的值是在編譯時(shí)確定的,因此在運(yùn)行時(shí)訪問(wèn)枚舉類型的值會(huì)更快。此外,由于枚舉類型中的常量值是唯一的,因此可以直接使用“==”進(jìn)行兩個(gè)值之間的對(duì)比,這有助于提高性能。
- 更好的組織性:枚舉類型可以幫助我們將相關(guān)的值組織在一起,使代碼更加整潔。通過(guò)將相關(guān)的常量值組合在一起,可以使代碼更加易于理解和維護(hù)。
- 可擴(kuò)展性:枚舉類型可以輕松地?cái)U(kuò)展或更新,而不會(huì)對(duì)其他部分的代碼造成影響。這有助于保持代碼的靈活性和可擴(kuò)展性。
- 便于測(cè)試:枚舉類型可以方便地進(jìn)行測(cè)試,因?yàn)樗鼈兙哂杏邢耷掖_定的值域。這使得測(cè)試人員可以更容易地覆蓋所有可能的場(chǎng)景,并確保代碼的正確性。
雖然枚舉有諸多的好處,但是使用枚舉也給我們帶來(lái)了一些困擾:
- 前后端數(shù)據(jù)格式轉(zhuǎn)換:前端主要給用戶展示數(shù)據(jù),不能直接顯示枚舉值,需要前端將枚舉轉(zhuǎn)成用戶可讀的數(shù)據(jù)顯示
- 數(shù)據(jù)庫(kù)的存儲(chǔ):代碼中的枚舉類型無(wú)法直接存儲(chǔ)數(shù)據(jù)庫(kù),一般轉(zhuǎn)成數(shù)值類型,這樣還可以減少存儲(chǔ)空間
- 代碼中大量類型轉(zhuǎn)換:查詢時(shí)需要數(shù)值類型轉(zhuǎn)成枚舉類型,保存時(shí)又需要將枚舉類型轉(zhuǎn)成數(shù)值類型
針對(duì)枚舉存在的問(wèn)題,本文介紹一種枚舉從數(shù)據(jù)庫(kù)-->后端代碼-->前端代碼-->頁(yè)面和從頁(yè)面-->前端代碼-->后端代碼-->數(shù)據(jù)庫(kù)的自動(dòng)轉(zhuǎn)換方案,大大方便前后端使用枚舉類型。
自動(dòng)轉(zhuǎn)換目標(biāo)
我們以用戶狀態(tài)為例,用戶有兩種狀態(tài):禁用和啟用
前端頁(yè)面:前端頁(yè)面顯示用戶狀態(tài)時(shí)用“禁用、啟用”;
前端代碼:前端代碼里處理用戶狀態(tài)時(shí)用:“ENABLE、DISABLE”或者用“0、1”;
后端代碼:后端代碼使用StatusEnum枚舉類;
數(shù)據(jù)庫(kù):數(shù)據(jù)庫(kù)存儲(chǔ)用戶狀態(tài)時(shí)禁用存1、啟用存0。

我們的目標(biāo)是讓枚舉在各個(gè)環(huán)境流轉(zhuǎn)時(shí)全自動(dòng)轉(zhuǎn)換。
代碼與數(shù)據(jù)庫(kù)自動(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ù)庫(kù)與實(shí)體對(duì)象中枚舉類型的相互轉(zhuǎn)換,如果只使用的Mybatis可以自定義TypeHandler來(lái)解決數(shù)據(jù)庫(kù)到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è)屬性都返回給前端,前端頁(yè)面顯示時(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)換問(wèn)題,其它枚舉只要實(shí)現(xiàn)BaseEnum接口就能實(shí)現(xiàn)全自動(dòng)轉(zhuǎn)換,前后端用起來(lái)也方便了不少。
總結(jié)
本文主要介紹了項(xiàng)目中使用枚舉的優(yōu)缺點(diǎn),并針對(duì)缺點(diǎn)給出了解決方案,解決了枚舉在項(xiàng)目中頻繁轉(zhuǎn)換的問(wèn)題,當(dāng)然解決的還不是非常完美,比如返回給前端的枚舉格式是:{"enum":"DISABLE","code":1} 但是保存時(shí)傳此數(shù)據(jù)結(jié)構(gòu),后端卻無(wú)法正確的轉(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ù)庫(kù)——mysql,需要的朋友可以參考下2022-10-10
SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能
這篇文章主要介紹了SpringBoot+Vue實(shí)現(xiàn)數(shù)據(jù)添加功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
SpringMVC中的HandlerMapping和HandlerAdapter詳解
這篇文章主要介紹了SpringMVC中的HandlerMapping和HandlerAdapter詳解,在Spring MVC中,HandlerMapping(處理器映射器)用于確定請(qǐng)求處理器對(duì)象,請(qǐng)求處理器可以是任何對(duì)象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下2023-08-08
Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決
這篇文章主要介紹了Springboot文件上傳出現(xiàn)找不到指定系統(tǒng)路徑的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
springboot項(xiàng)目配置context path失效的問(wèn)題解決
本文主要介紹了springboot項(xiàng)目配置context path失效的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
基于Hibernate中配置文件的學(xué)習(xí)(分享)
下面小編就為大家?guī)?lái)一篇基于Hibernate中配置文件的學(xué)習(xí)(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06

