亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

優(yōu)雅地在Java應(yīng)用中實(shí)現(xiàn)全局枚舉處理的方法

 更新時(shí)間:2019年02月12日 11:10:55   作者:史亞健  
這篇文章主要給大家介紹了關(guān)于如何優(yōu)雅地在Java應(yīng)用中實(shí)現(xiàn)全局枚舉處理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

背景描述

為了表達(dá)某一個(gè)屬性,具備一組可選的范圍,我們一般會(huì)采用兩種方式。枚舉類(lèi)和數(shù)據(jù)字典,兩者具有各自的優(yōu)點(diǎn)。枚舉類(lèi)寫(xiě)在Java代碼中,方便編寫(xiě)相應(yīng)的判斷邏輯,代碼可讀性高,枚舉類(lèi)中的屬性是可提前預(yù)估和確定的。數(shù)據(jù)字典,一般保存在數(shù)據(jù)庫(kù),不便于編寫(xiě)判斷和分支邏輯,因?yàn)閿?shù)據(jù)如果有所變動(dòng),那么對(duì)應(yīng)的代碼邏輯很有可能失效,強(qiáng)依賴數(shù)據(jù)庫(kù)數(shù)據(jù)的正確性,數(shù)據(jù)字典中對(duì)應(yīng)的屬性對(duì)業(yè)務(wù)影響并不大,日常開(kāi)發(fā)中常用做分類(lèi),打標(biāo)簽使用,屬性的多少無(wú)法估計(jì)。

目前基本上沒(méi)有一個(gè)很好的全局處理枚舉類(lèi)的方案,所以我就自己綜合各方面資料寫(xiě)了一個(gè)。

代碼

架構(gòu)還在不斷完善中,代碼不一定可以跑起來(lái),不過(guò)關(guān)于枚舉的配置已經(jīng)完成,大家可以閱讀并參考借鑒:pretty-demo

前言

大多數(shù)公司處理枚舉的時(shí)候,會(huì)自定義一個(gè)枚舉轉(zhuǎn)換工具類(lèi),或者在枚舉類(lèi)中編寫(xiě)一個(gè)靜態(tài)方法實(shí)現(xiàn)Integer轉(zhuǎn)換枚舉的方式。

比如:

// 靜態(tài)方法方式
public enum GenderEnum {

  // 代碼略
 
  public static GenderEnum get(int value) {
   for (GenderEnum item : GenderEnum.values()) {
   if (value == item.getValue()) {
     return item;
    }
   }
   return null;
  }
}
// 工具類(lèi)方式
public class EnumUtil {

 public static <E extends Enumerable> E of(@Nonnull Class<E> classType, int value) {
  for (E enumConstant : classType.getEnumConstants()) {
   if (value == enumConstant.getValue()) {
    return enumConstant;
   }
  }
  return null;
 }

}

GenderEnum gender = EnumUtil.of(GenderEnum.class,1);

這種方式很麻煩,或者需要手動(dòng)編寫(xiě)對(duì)應(yīng)的靜態(tài)方法,或者需要手動(dòng)調(diào)用工具類(lèi)進(jìn)行轉(zhuǎn)換。

解決方案

為了方便起見(jiàn),我做了一個(gè)全局枚舉值轉(zhuǎn)換的方案,這個(gè)方案可以實(shí)現(xiàn)前端通過(guò)傳遞int到服務(wù)端,服務(wù)端自動(dòng)轉(zhuǎn)換成枚舉類(lèi),進(jìn)行相應(yīng)的業(yè)務(wù)判斷之后,再以數(shù)字的形式存到數(shù)據(jù)庫(kù);我們?cè)诓閿?shù)據(jù)的時(shí)候,又能將數(shù)據(jù)庫(kù)的數(shù)字轉(zhuǎn)換成java枚舉類(lèi),在處理完對(duì)應(yīng)的業(yè)務(wù)邏輯之后,將枚舉和枚舉類(lèi)對(duì)應(yīng)的展示信息一起傳遞到前臺(tái),前臺(tái)不需要維護(hù)這個(gè)枚舉類(lèi)和展示信息的對(duì)應(yīng)關(guān)系,同時(shí)展示信息支持國(guó)際化處理,具體的方案如下:

1、基于約定大于配置的原則,制定統(tǒng)一的枚舉類(lèi)的編寫(xiě)規(guī)則。大概規(guī)則如下:

  • 每個(gè)枚舉類(lèi)有兩個(gè)字段: int value(存數(shù)據(jù)庫(kù)),String key(通過(guò)key找對(duì)應(yīng)的i18n文本信息)。這塊需要細(xì)細(xì)討論下,枚舉值通常存數(shù)據(jù)庫(kù)有存int值,也有存String值,各有利弊。存int的好處就是體積小,如果枚舉的值是包含規(guī)律的,比如-1是刪除,0是預(yù)處理,1是處理,2是處理完成,那么我們所有非刪除數(shù)據(jù),我們可以不使用 status in ( 0,1,2)這種方式,而轉(zhuǎn)換為 status >= 0 ; 存String的話,好處就是可讀性高,直接能從數(shù)據(jù)庫(kù)的值中明白對(duì)應(yīng)的狀態(tài),劣勢(shì)就是占的體積大點(diǎn)。當(dāng)然這些都是相對(duì)的,存int的時(shí)候,我們可以完善好注釋,也具備好的可讀性。如果int換成String,占的體積多的那一點(diǎn),其實(shí)也可以忽略不計(jì)的。
  • 枚舉枚舉類(lèi)需要繼承統(tǒng)一接口,提供相應(yīng)的方法供通用處理枚舉時(shí)候使用。

下面是枚舉接口和一個(gè)枚舉示例:

public interface Enumerable<E extends Enumerable> {

 /**
  * 獲取在i18n文件中對(duì)應(yīng)的 key
  * @return key
  */
 @Nonnull
 String getKey();

 /**
  * 獲取最終保存到數(shù)據(jù)庫(kù)的值
  * @return 值
  */
 @Nonnull
 int getValue();

 /**
  * 獲取 key 對(duì)應(yīng)的文本信息
  * @return 文本信息
  */
 @Nonnull
 default String getText() {
  return I18nMessageUtil.getMessage(this.getKey(), null);
 }
}
public enum GenderEnum implements Enumerable {

 /** 男 */
 MALE(1, "male"),

 /** 女 */
 FEMALE(2, "female");

 private int value;

 private String key;

 GenderEnum(int value, String key) {
  this.value = value;
  this.key = key;
 }

 @Override
 public String getKey() {
  return this.key;
 }

 @Override
 public int getValue() {
  return this.value;
 }
}

我們要做的就是,每個(gè)我們編寫(xiě)的枚舉類(lèi),都需要按這樣的方式進(jìn)行編寫(xiě),按照規(guī)范定義的枚舉類(lèi)方便下面統(tǒng)一編寫(xiě)。

2、我們分析下controller層面的數(shù)據(jù)進(jìn)和出,從而處理好枚舉類(lèi)和int值的轉(zhuǎn)換,在Spring MVC中,框架幫我們做了數(shù)據(jù)類(lèi)型的轉(zhuǎn)換,所以我們以 Spring MVC作為切入點(diǎn)。前臺(tái)發(fā)送到服務(wù)端的請(qǐng)求,一般有參數(shù)在url中和body中兩種方式為主,分別以get請(qǐng)求和post請(qǐng)求配合@RequestBody為代表。

【入?yún)ⅰ縢et方法為代表,請(qǐng)求的MediaType為"application/x-www-form-urlencoded",此時(shí)將 int 轉(zhuǎn)換成枚舉,我們注冊(cè)一個(gè)新的Converter,如果spring MVC判斷到一個(gè)值要轉(zhuǎn)換成我們定義的枚舉類(lèi)對(duì)象時(shí),調(diào)用我們?cè)O(shè)定的這個(gè)轉(zhuǎn)換器

@Configuration
public class MvcConfiguration implements WebMvcConfigurer, WebBindingInitializer {

 /**
  * [get]請(qǐng)求中,將int值轉(zhuǎn)換成枚舉類(lèi)
  * @param registry
  */
 @Override
 public void addFormatters(FormatterRegistry registry) {
  registry.addConverterFactory(new EnumConverterFactory());
 }
}

public class EnumConverterFactory implements ConverterFactory<String, Enumerable> {

 private final Map<Class, Converter> converterCache = new WeakHashMap<>();

 @Override
 @SuppressWarnings({"rawtypes", "unchecked"})
 public <T extends Enumerable> Converter<String, T> getConverter(@Nonnull Class<T> targetType) {
  return converterCache.computeIfAbsent(targetType,
    k -> converterCache.put(k, new EnumConverter(k))
  );
 }

 protected class EnumConverter<T extends Enumerable> implements Converter<Integer, T> {

  private final Class<T> enumType;

  public EnumConverter(@Nonnull Class<T> enumType) {
   this.enumType = enumType;
  }

  @Override
  public T convert(@Nonnull Integer value) {
   return EnumUtil.of(this.enumType, value);
  }
 }
}

【入?yún)ⅰ縫ost為代表,將 int 轉(zhuǎn)換成枚舉。這塊我們和前臺(tái)達(dá)成一個(gè)約定( Ajax中applicationType),所有在body中的數(shù)據(jù)必須為json格式。同樣后臺(tái)@RequestBody對(duì)應(yīng)的參數(shù)的請(qǐng)求的MediaType為"application/json",spring MVC中對(duì)于Json格式的數(shù)據(jù),默認(rèn)使用 Jackson2HttpMessageConverter。在Jackson轉(zhuǎn)換成實(shí)體時(shí)候,有@JsonCreator和@JsonValue兩個(gè)注解可以用,但是感覺(jué)還是有點(diǎn)麻煩。為了統(tǒng)一處理,我們需要修改Jackson對(duì)枚舉類(lèi)的序列化和反序列的支持。配置如下:

@Configuration
@Slf4j
public class JacksonConfiguration {

 /**
  * Jackson的轉(zhuǎn)換器
  * @return
  */
 @Bean
 @Primary
 @SuppressWarnings({"rawtypes", "unchecked"})
 public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
  final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  ObjectMapper objectMapper = converter.getObjectMapper();
  // Include.NON_EMPTY 屬性為 空("") 或者為 NULL 都不序列化,則返回的json是沒(méi)有這個(gè)字段的。這樣對(duì)移動(dòng)端會(huì)更省流量
  objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
  // 反序列化時(shí)候,遇到多余的字段不失敗,忽略
  objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  // 允許出現(xiàn)特殊字符和轉(zhuǎn)義符
  objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
  // 允許出現(xiàn)單引號(hào)
  objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
  SimpleModule customerModule = new SimpleModule();
  customerModule.addDeserializer(String.class, new StringTrimDeserializer(String.class));
  customerModule.addDeserializer(Enumerable.class, new EnumDeserializer(Enumerable.class));
  customerModule.addSerializer(Enumerable.class, new EnumSerializer(Enumerable.class));
  objectMapper.registerModule(customerModule);
  converter.setSupportedMediaTypes(ImmutableList.of(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
  return converter;
 }

}
public class EnumDeserializer<E extends Enumerable> extends StdDeserializer<E> {

 private Class<E> enumType;

 public EnumDeserializer(@Nonnull Class<E> enumType) {
  super(enumType);
  this.enumType = enumType;
 }

 @Override
 public E deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
  return EnumUtil.of(this.enumType, jsonParser.getIntValue());
 }

}

【出參】當(dāng)我們查詢出結(jié)果,要展示給前臺(tái)的時(shí)候,我們會(huì)對(duì)結(jié)果集增加@ResponseBody注解,這時(shí)候會(huì)調(diào)用Jackson的序列化方法,所以我們?cè)黾恿嗣杜e類(lèi)的序列配置。如果我們只簡(jiǎn)單的將枚舉轉(zhuǎn)換成 int 給前臺(tái),那么前臺(tái)需要維護(hù)這個(gè)枚舉類(lèi)的 int 和對(duì)應(yīng)展示信息的關(guān)系。所以這塊我們將值和展示信息一同返給前臺(tái),減輕前臺(tái)的工作壓力。

// 注冊(cè)枚舉類(lèi)序列化處理類(lèi)
customerModule.addSerializer(Enumerable.class, new EnumSerializer(Enumerable.class));

public class EnumSerializer extends StdSerializer<Enumerable> {

 public EnumSerializer(@Nonnull Class<Enumerable> type) {
  super(type);
 }

 @Override
 public void serialize(Enumerable enumerable, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
  jsonGenerator.writeStartObject();
  jsonGenerator.writeNumberField("value", enumerable.getValue());
  jsonGenerator.writeStringField("text", enumerable.getText());
  jsonGenerator.writeEndObject();
 }
}

這樣關(guān)于入?yún)⒑统鰠⒌呐渲枚纪瓿闪耍覀兛梢员WC,所有前臺(tái)傳遞到后臺(tái)的 int 都會(huì)自動(dòng)轉(zhuǎn)換成枚舉類(lèi)。如果返回的數(shù)據(jù)有枚舉類(lèi),枚舉類(lèi)也會(huì)包含值和展示文本,方便簡(jiǎn)單。

3、存儲(chǔ)層關(guān)于枚舉類(lèi)的轉(zhuǎn)換。這里選的 ORM 框架為 Mybatis ,但是你如果翻看官網(wǎng),官網(wǎng)的資料只提供了兩個(gè)方案,就是通過(guò)枚舉隱藏字段name和ordinal的轉(zhuǎn)換,沒(méi)有一個(gè)通用枚舉的解決方案。但是通過(guò)翻看 github 中的 issue release 記錄,發(fā)現(xiàn)在 3.4.5版本中就提供了對(duì)應(yīng)的自定義枚舉處理配置,這塊不需要我們做過(guò)多的配置,我們直接增加 mybatis-spring-boot-starter 的依賴,直接配置對(duì)應(yīng)的Yaml 文件就實(shí)現(xiàn)了功能。

application.yml
--
mybatis:
 configuration:
  default-enum-type-handler: github.shiyajian.pretty.config.enums.EnumTypeHandler
public class EnumTypeHandler<E extends Enumerable> extends BaseTypeHandler<E> {

  private Class<E> enumType;

  public EnumTypeHandler() { /* instance */ }


  public EnumTypeHandler(@Nonnull Class<E> enumType) {
    this.enumType = enumType;
  }

  @Override
  public void setNonNullParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {
    preparedStatement.setInt(i, e.getValue());
  }

  @Override
  public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
    int value = rs.getInt(columnName);
    return rs.wasNull() ? null : EnumUtil.of(this.enumType, value);
  }

  @Override
  public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    int value = rs.getInt(columnIndex);
    return rs.wasNull() ? null : EnumUtil.of(this.enumType, value);
  }

  @Override
  public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    int value = cs.getInt(columnIndex);
    return cs.wasNull() ? null : EnumUtil.of(this.enumType, value);
  }

}

這樣我們就完成了從前臺(tái)頁(yè)面到業(yè)務(wù)代碼到數(shù)據(jù)庫(kù)的存儲(chǔ),從數(shù)據(jù)庫(kù)查詢到業(yè)務(wù)代碼再到頁(yè)面的枚舉類(lèi)轉(zhuǎn)換。整個(gè)項(xiàng)目中完全不需要再手動(dòng)去處理枚舉類(lèi)了。我們的開(kāi)發(fā)流程簡(jiǎn)單了很多。

結(jié)語(yǔ)

一個(gè)好的方案并不需要多么高大上的技術(shù),比如各種反射,各種設(shè)計(jì)模式,只要設(shè)計(jì)合理,就是簡(jiǎn)單易用,類(lèi)似中國(guó)古代的榫卯。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • 詳解AngularJs與SpringMVC簡(jiǎn)單結(jié)合使用

    詳解AngularJs與SpringMVC簡(jiǎn)單結(jié)合使用

    本篇文章主要介紹了AngularJs與SpringMVC簡(jiǎn)單結(jié)合使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • JVM知識(shí)總結(jié)之垃圾收集算法

    JVM知識(shí)總結(jié)之垃圾收集算法

    本博客為讀書(shū)筆記,讀的是《深入理解Java虛擬機(jī)》一書(shū),在看這個(gè)書(shū)的時(shí)候,最大的一個(gè)感受便是“當(dāng)初怎么就沒(méi)有好好學(xué)習(xí)操作系統(tǒng)呢,不然也不會(huì)有這么多看的云里霧里的地方了”,不過(guò)那都是過(guò)去的事了,學(xué)習(xí)最好的時(shí)刻便是現(xiàn)在,需要的朋友可以參考下
    2021-06-06
  • java實(shí)現(xiàn)簡(jiǎn)單銀行ATM系統(tǒng)

    java實(shí)現(xiàn)簡(jiǎn)單銀行ATM系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單銀行ATM系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理

    Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理

    這篇文章主要介紹Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理,負(fù)載均衡能夠平均分配客戶請(qǐng)求到服 務(wù)器陣列,借此提供快速獲取重要數(shù)據(jù),解決大量并發(fā)訪問(wèn)服務(wù)問(wèn)題,這種集群技術(shù)可以用最少的投資獲得接近于大型主機(jī)的性能。下面就來(lái)看看文章的具體內(nèi)容吧
    2021-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)詳解

    Java數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)詳解

    二叉搜索樹(shù)作為一個(gè)經(jīng)典的數(shù)據(jù)結(jié)構(gòu),具有鏈表的快速插入與刪除的特點(diǎn),同時(shí)查詢效率也很優(yōu)秀,所以應(yīng)用十分廣泛。本文將詳細(xì)講講二叉搜索樹(shù)的原理與實(shí)現(xiàn),需要的可以參考一下
    2022-06-06
  • java按指定編碼寫(xiě)入和讀取文件內(nèi)容的類(lèi)分享

    java按指定編碼寫(xiě)入和讀取文件內(nèi)容的類(lèi)分享

    這篇文章主要介紹了java按指定編碼寫(xiě)入和讀取文件內(nèi)容的類(lèi),需要的朋友可以參考下
    2014-02-02
  • java中的hashCode方法小例子

    java中的hashCode方法小例子

    這篇文章主要介紹了java中的hashCode方法小例子,有需要的朋友可以參考一下
    2013-12-12
  • Spring Cloud應(yīng)用實(shí)現(xiàn)配置自動(dòng)刷新過(guò)程詳解

    Spring Cloud應(yīng)用實(shí)現(xiàn)配置自動(dòng)刷新過(guò)程詳解

    這篇文章主要介紹了Spring Cloud應(yīng)用實(shí)現(xiàn)配置自動(dòng)刷新過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • OpenFeign實(shí)現(xiàn)微服務(wù)間的文件下載方式

    OpenFeign實(shí)現(xiàn)微服務(wù)間的文件下載方式

    這篇文章主要介紹了OpenFeign實(shí)現(xiàn)微服務(wù)間的文件下載方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • maven國(guó)內(nèi)鏡像配置的方法步驟

    maven國(guó)內(nèi)鏡像配置的方法步驟

    這篇文章主要介紹了maven國(guó)內(nèi)鏡像配置的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07

最新評(píng)論