女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

看看人家在接口中使用枚舉類型的方式

jf_ro2CN3Fa ? 來源:geekhalo ? 2023-02-24 13:59 ? 次閱讀

1. 概覽

枚舉作為 Java 5 的重要特征,相信大家并不陌生,但在實際開發過程中,當 name 和 ordrial 發生變化時,如果處理不當非常容易引起系統bug。這種兼容性bug非常難以定位,需要從框架層次進行避免,而非僅靠開發人員的主觀意識。

1.1. 背景

枚舉很好用,特別是提供的 name 和 ordrial 特性,但這點對重構造成了一定影響,比如:

某個枚舉值業務語義發生變化,需要將其進行 rename 操作,以更好的表達新業務語義

新增、刪除或者為了展示調整了枚舉定義順序

這些在業務開發中非常常見,使用 IDE 的 refactor 功能可以快速且準確的完成重構工作。但,如果系統將這些暴露出去或者存儲到數據庫等存儲引擎就變得非常麻煩,不管是 name 還是 ordrial 的變更都會產生兼容性問題。

對此,最常見的解決方案便是放棄使用 name 和 ordrial,轉而使用控制能力更強的 code。

1.2. 目標

提供一組工具,以方便的基于 code 使用枚舉,快速完成對現有框架的集成:

完成與 Spring MVC 的集成,基于 code 使用枚舉;加強返回值,以對象的方式進行返回,信息包括 code、name、description

提供統一的枚舉字典,自動掃描系統中的枚舉并將其以 restful 的方式暴露給前端

使用 code 進行數據存儲操作,避免重構的影響

2. 快速入門

2.1. 添加 starter

在 Spring boot 項目的 pom 中增加如下依賴:

com.geekhalo.lego
lego-starter
0.1.19-enum-SNAPSHOT

2.2. 統一枚舉結構

如何統一枚舉行為呢?公共父類肯定是不行的,但可以為其提供一個接口,在接口中完成行為的定義。

2.2.1. 定義枚舉接口

除了在枚舉中自定義 code 外,通常還會為其提供描述信息,構建接口如下:

publicinterfaceCodeBasedEnum{
intgetCode();
}
publicinterfaceSelfDescribedEnum{
defaultStringgetName(){
returnname();
}

Stringname();

StringgetDescription();
}
publicinterfaceCommonEnumextendsCodeBasedEnum,SelfDescribedEnum{

}

整體結構如下:

7cabccf2-b37f-11ed-bfe3-dac502259ad0.png

在定義枚舉時便可以直接使用CommonEnum這個接口。

2.2.2. 實現枚舉接口

有了統一的枚舉接口,在定義枚舉時便可以直接實現接口,從而完成對枚舉的約束。

publicenumNewsStatusimplementsCommonEnum{
DELETE(1,"刪除"),
ONLINE(10,"上線"),
OFFLINE(20,"下線");
privatefinalintcode;
privatefinalStringdesc;

NewsStatus(intcode,Stringdesc){
this.code=code;
this.desc=desc;
}

@Override
publicintgetCode(){
returnthis.code;
}

@Override
publicStringgetDescription(){
returnthis.desc;
}
}

2.3. 自動注冊 CommonEnum

有了統一的 CommonEnum 最大的好處便是可以進行統一管理,對于統一管理,第一件事便是找到并注冊所有的 CommonEnum。

7cbbd3d6-b37f-11ed-bfe3-dac502259ad0.png

以上是核心處理流程:

首先通過 Spring 的 ResourcePatternResolver 根據配置的 basePackage 對classpath進行掃描

掃描結果以Resource來表示,通過 MetadataReader 讀取 Resource 信息,并將其解析為 ClassMetadata

獲得 ClassMetadata 之后,找出實現 CommonEnum 的類

將 CommonEnum 實現類注冊到兩個 Map 中進行緩存

備注:此處萬萬不可直接使用反射技術,反射會觸發類的自動加載,將對眾多不需要的類進行加載,從而增加 metaspace 的壓力。

在需要 CommonEnum 時,只需注入 CommonEnumRegistry Bean 便可以方便的獲得 CommonEnum 的具體實現。

2.4. Spring MVC 接入層

Web 層是最常見的接入點,對于 CommonEnum 我們傾向于:

參數使用 code 來表示,避免 name、ordrial 變化導致業務異常

豐富返回值,包括枚舉的 code、name、description 等

7ce61fba-b37f-11ed-bfe3-dac502259ad0.png

2.4.1. 入參轉化

Spring MVC 存在兩種參數轉化擴展:

對于普通參數,比如 RequestParam 或 PathVariable 直接從 ConditionalGenericConverter 進行擴展

基于 CommonEnumRegistry 提供的 CommonEnum 信息,對 matches 和 getConvertibleTypes方法進行重寫

根據目標類型獲取所有的 枚舉值,并根據 code 和 name 進行轉化

對于 Json 參數,需要對 Json 框架進行擴展(以 Jackson 為例)

遍歷 CommonEnumRegistry 提供的所有 CommonEnum,依次進行注冊

從 Json 中讀取信息,根據 code 和 name 轉化為確定的枚舉值

兩種擴展核心實現見:

@Order(1)
@Component
publicclassCommonEnumConverterimplementsConditionalGenericConverter{
@Autowired
privateCommonEnumRegistryenumRegistry;

@Override
publicbooleanmatches(TypeDescriptorsourceType,TypeDescriptortargetType){
Classtype=targetType.getType();
returnenumRegistry.getClassDict().containsKey(type);
}

@Override
publicSetgetConvertibleTypes(){
returnenumRegistry.getClassDict().keySet().stream()
.map(cls->newConvertiblePair(String.class,cls))
.collect(Collectors.toSet());
}

@Override
publicObjectconvert(Objectsource,TypeDescriptorsourceType,TypeDescriptortargetType){
Stringvalue=(String)source;
ListcommonEnums=this.enumRegistry.getClassDict().get(targetType.getType());
returncommonEnums.stream()
.filter(commonEnum->commonEnum.match(value))
.findFirst()
.orElse(null);
}
}

staticclassCommonEnumJsonDeserializerextendsJsonDeserializer{
privatefinalListcommonEnums;

CommonEnumJsonDeserializer(ListcommonEnums){
this.commonEnums=commonEnums;
}

@Override
publicObjectdeserialize(JsonParserjsonParser,DeserializationContextdeserializationContext)throwsIOException,JacksonException{
Stringvalue=jsonParser.readValueAs(String.class);
returncommonEnums.stream()
.filter(commonEnum->commonEnum.match(value))
.findFirst()
.orElse(null);
}
}

2.4.2. 增強返回值

默認情況下,對于枚舉類型在轉換為 Json 時,只會輸出 name,其他信息會出現丟失,對于展示非常不友好,對此,需要對 Json 序列化進行能力增強。

首先,需要定義 CommonEnum 對應的返回對象,具體如下:

@Value
@AllArgsConstructor(access=AccessLevel.PRIVATE)
@ApiModel(description="通用枚舉")
publicclassCommonEnumVO{
@ApiModelProperty(notes="Code")
privatefinalintcode;

@ApiModelProperty(notes="Name")
privatefinalStringname;

@ApiModelProperty(notes="描述")
privatefinalStringdesc;

publicstaticCommonEnumVOfrom(CommonEnumcommonEnum){
if(commonEnum==null){
returnnull;
}
returnnewCommonEnumVO(commonEnum.getCode(),commonEnum.getName(),commonEnum.getDescription());
}

publicstaticListfrom(ListcommonEnums){
if(CollectionUtils.isEmpty(commonEnums)){
returnCollections.emptyList();
}
returncommonEnums.stream()
.filter(Objects::nonNull)
.map(CommonEnumVO::from)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

CommonEnumVO 是一個標準的 POJO,只是增加了 Swagger 相關注解。

CommonEnumJsonSerializer 是自定義序列化的核心,會將 CommonEnum 封裝為 CommonEnumVO 并進行寫回,具體如下:

staticclassCommonEnumJsonSerializerextendsJsonSerializer{

@Override
publicvoidserialize(Objecto,JsonGeneratorjsonGenerator,SerializerProviderserializerProvider)throwsIOException{
CommonEnumcommonEnum=(CommonEnum)o;
CommonEnumVOcommonEnumVO=CommonEnumVO.from(commonEnum);
jsonGenerator.writeObject(commonEnumVO);
}
}

2.4.3. 效果展示

首先,新建一個測試枚舉 NewsStatus,具體如下:

publicenumNewsStatusimplementsCommonEnum{
DELETE(1,"刪除"),
ONLINE(10,"上線"),
OFFLINE(20,"下線");
privatefinalintcode;
privatefinalStringdesc;

NewsStatus(intcode,Stringdesc){
this.code=code;
this.desc=desc;
}

@Override
publicintgetCode(){
returnthis.code;
}

@Override
publicStringgetDescription(){
returnthis.desc;
}
}

然后新建 EnumController,具體如下:

@RestController
@RequestMapping("enum")
publicclassEnumController{
@GetMapping("paramToEnum")
publicRestResultparamToEnum(@RequestParam("newsStatus")NewsStatusnewsStatus){
returnRestResult.success(CommonEnumVO.from(newsStatus));
}

@GetMapping("pathToEnum/{newsStatus}")
publicRestResultpathToEnum(@PathVariable("newsStatus")NewsStatusnewsStatus){
returnRestResult.success(CommonEnumVO.from(newsStatus));
}

@PostMapping("jsonToEnum")
publicRestResultjsonToEnum(@RequestBodyNewsStatusRequestBodynewsStatusRequestBody){
returnRestResult.success(CommonEnumVO.from(newsStatusRequestBody.getNewsStatus()));
}

@GetMapping("bodyToJson")
publicRestResultbodyToJson(){
NewsStatusResponseBodynewsStatusResponseBody=newNewsStatusResponseBody();
newsStatusResponseBody.setNewsStatus(Arrays.asList(NewsStatus.values()));
returnRestResult.success(newsStatusResponseBody);
}

@Data
publicstaticclassNewsStatusRequestBody{
privateNewsStatusnewsStatus;
}

@Data
publicstaticclassNewsStatusResponseBody{
privateListnewsStatus;
}
}

執行結果如下:

7d89d560-b37f-11ed-bfe3-dac502259ad0.png

整體符合預期:

使用 code 作為請求參數可以自動轉化為對應的 CommonEnum

使用 CommonEnum 作為返回值,返回標準的 CommonEnumVO 對象結構

2.5. 通用枚舉字典接口

有時可以將 枚舉 理解為系統的一類字段,比較典型的就是管理頁面的各種下拉框,下拉框中的數據來自于后臺服務。

有了 CommonEnum 之后,可以提供統一的一組枚舉字典,避免重復開發,同時在新增枚舉時也無需進行擴展,系統自動識別并添加到字典中。

2.5.1. 構建字典Controller

在 CommonEnumRegistry 基礎之上實現通用字典接口非常簡單,只需按規范構建 Controller 即可,具體如下:

@Api(tags="通用字典接口")
@RestController
@RequestMapping("/enumDict")
@Slf4j
publicclassEnumDictController{
@Autowired
privateCommonEnumRegistrycommonEnumRegistry;

@GetMapping("all")
publicRestResult>>allEnums(){
Map>dict=this.commonEnumRegistry.getNameDict();
Map>dictVo=Maps.newHashMapWithExpectedSize(dict.size());
for(Map.Entry>entry:dict.entrySet()){
dictVo.put(entry.getKey(),CommonEnumVO.from(entry.getValue()));
}
returnRestResult.success(dictVo);
}

@GetMapping("types")
publicRestResult>enumTypes(){
Map>dict=this.commonEnumRegistry.getNameDict();
returnRestResult.success(Lists.newArrayList(dict.keySet()));
}

@GetMapping("/{type}")
publicRestResult>dictByType(@PathVariable("type")Stringtype){
Map>dict=this.commonEnumRegistry.getNameDict();
ListcommonEnums=dict.get(type);

returnRestResult.success(CommonEnumVO.from(commonEnums));
}
}

該 Controller 提供如下能力:

獲取全部字典,一次性獲取系統中所有的 CommonEnum

獲取所有字典類型,僅獲取字典類型,通常用于測試

獲取指定字典類型的全部信息,比如上述所說的填充下拉框

2.5.2. 效果展示

獲取全部字典:

7e020882-b37f-11ed-bfe3-dac502259ad0.png

獲取所有字典類型:

7e15eece-b37f-11ed-bfe3-dac502259ad0.png

獲取指定字段類型的全部信息:

7e2409d2-b37f-11ed-bfe3-dac502259ad0.png

2.6. 輸出適配器

輸出適配器主要以 ORM 框架為主,同時各類 ORM 框架均提供了類型映射的擴展點,通過該擴展點可以對 CommonEnum 使用 code 進行存儲。

2.6.1. MyBatis 支持

MyBatis 作為最流行的 ORM 框架,提供了 TypeHandler 用于處理自定義的類型擴展。

@MappedTypes(NewsStatus.class)
publicclassMyBatisNewsStatusHandlerextendsCommonEnumTypeHandler{
publicMyBatisNewsStatusHandler(){
super(NewsStatus.values());
}
}

MyBatisNewsStatusHandler 通過 @MappedTypes(NewsStatus.class) 對其進行標記,以告知框架該 Handler 是用于 NewsStatus 類型的轉換。

CommonEnumTypeHandler 是為 CommonEnum 提供的通用轉化能力,具體如下:

publicabstractclassCommonEnumTypeHandler&CommonEnum>
extendsBaseTypeHandler{
privatefinalListcommonEnums;

protectedCommonEnumTypeHandler(T[]commonEnums){
this(Arrays.asList(commonEnums));
}

protectedCommonEnumTypeHandler(ListcommonEnums){
this.commonEnums=commonEnums;
}

@Override
publicvoidsetNonNullParameter(PreparedStatementpreparedStatement,inti,Tt,JdbcTypejdbcType)throwsSQLException{
preparedStatement.setInt(i,t.getCode());
}

@Override
publicTgetNullableResult(ResultSetresultSet,StringcolumnName)throwsSQLException{

intcode=resultSet.getInt(columnName);
returncommonEnums.stream()
.filter(commonEnum->commonEnum.match(String.valueOf(code)))
.findFirst()
.orElse(null);
}

@Override
publicTgetNullableResult(ResultSetresultSet,inti)throwsSQLException{
intcode=resultSet.getInt(i);
returncommonEnums.stream()
.filter(commonEnum->commonEnum.match(String.valueOf(code)))
.findFirst()
.orElse(null);
}

@Override
publicTgetNullableResult(CallableStatementcallableStatement,inti)throwsSQLException{
intcode=callableStatement.getInt(i);
returncommonEnums.stream()
.filter(commonEnum->commonEnum.match(String.valueOf(code)))
.findFirst()
.orElse(null);
}
}

由于邏輯比較簡單,在此不做過多解釋。

有了類型之后,需要在 spring boot 的配置文件中指定 type-handler 的加載邏輯,具體如下:

mybatis:
type-handlers-package:com.geekhalo.lego.enums.mybatis

完成配置后,使用 Mapper 對數據進行持久化,數據表中存儲的便是 code 信息,具體如下:

7e326b76-b37f-11ed-bfe3-dac502259ad0.png

2.6.2. JPA 支持

隨著 Spring data 越來越流行,JPA 又煥發出新的活力,JPA 提供 AttributeConverter 以對屬性轉換進行自定義。

首先,構建 JpaNewsStatusConverter,具體如下:

publicclassJpaNewsStatusConverterextendsCommonEnumAttributeConverter{
publicJpaNewsStatusConverter(){
super(NewsStatus.values());
}
}

CommonEnumAttributeConverter 為 CommonEnum 提供的通用轉化能力,具體如下:

publicabstractclassCommonEnumAttributeConverter&CommonEnum>
implementsAttributeConverter{
privatefinalListcommonEnums;

publicCommonEnumAttributeConverter(E[]commonEnums){
this(Arrays.asList(commonEnums));
}

publicCommonEnumAttributeConverter(ListcommonEnums){
this.commonEnums=commonEnums;
}

@Override
publicIntegerconvertToDatabaseColumn(Ee){
returne.getCode();
}

@Override
publicEconvertToEntityAttribute(Integercode){
return(E)commonEnums.stream()
.filter(commonEnum->commonEnum.match(String.valueOf(code)))
.findFirst()
.orElse(null);
}
}

在有了 JpaNewsStatusConverter 之后,我們需要在 Entity 的屬性上增加配置信息,具體如下:

@Entity
@Data
@Table(name="t_jpa_news")
publicclassJpaNewsEntity{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
privateLongid;

@Convert(converter=JpaNewsStatusConverter.class)
privateNewsStatusstatus;
}

@Convert(converter = JpaNewsStatusConverter.class) 是對 status 的配置,使用 JpaNewsStatusConverter 進行屬性的轉換。

運行持久化指令后,數據庫如下:

7e45c996-b37f-11ed-bfe3-dac502259ad0.png






審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 適配器
    +關注

    關注

    9

    文章

    2018

    瀏覽量

    69264
  • JAVA
    +關注

    關注

    20

    文章

    2984

    瀏覽量

    106788
  • MVC
    MVC
    +關注

    關注

    0

    文章

    73

    瀏覽量

    14054
  • JSON
    +關注

    關注

    0

    文章

    121

    瀏覽量

    7256

原文標題:看看人家在接口中使用枚舉類型的方式,那叫一個優雅!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    使用枚舉類型表示狀態機進入死循環

    定義狀態機中的狀態時,除了可以使用宏(define)或者參數(parameter)聲明定義外,還可以使用枚舉類型
    的頭像 發表于 11-07 17:46 ?1277次閱讀
    使用<b class='flag-5'>枚舉</b><b class='flag-5'>類型</b>表示狀態機進入死循環

    詳細講解c語言enum枚舉類型

    詳細講解C語言enum枚舉類型實際應用中,有的變量只有幾種可能取值。如人的性別只有兩種可能取值,星期只有七種可能取值。 C 語言中對這樣取值比較特殊的變量可以定義為
    發表于 12-21 23:22

    枚舉enum的使用

    }SysError_TypeDef;//創建一個枚舉變量就可以這樣SysError_TypeDefSysError;個人比較喜歡第三種方式。另外就是使用過程中,遇到這樣一個情況。要列舉出系統錯誤
    發表于 02-06 15:09

    深入理解java枚舉類型enum用法

    enum 的全稱為 enumeration, 是 JDK 1.5 中引入的新特性,存放在 java.lang 包中。 下面是我使用 enum 過程中的一些經驗和總結。 原始的接口定義常量
    發表于 09-27 11:49 ?0次下載

    枚舉是C語言中的一種基本數據類型

    C語言中,枚舉類型是被當做 int 或者 unsigned int 類型來處理的,所以按照 C 語言規范是沒有辦法遍歷枚舉
    的頭像 發表于 09-25 15:45 ?8856次閱讀
    <b class='flag-5'>枚舉</b>是C語言中的一種基本數據<b class='flag-5'>類型</b>

    go語言枚舉類型怎么用

    go 語言枚舉類型是這么用的?什么場景下會用到枚舉?本文對 go 語言枚舉做了詳細講解。 枚舉
    的頭像 發表于 09-02 09:43 ?5398次閱讀

    數字硬件建模SystemVerilog-枚舉數據類型

    上一節介紹了已經被淘汰的$unit聲明空間,今天我們來看看一種重要的數據類型-枚舉數據類型
    的頭像 發表于 07-01 17:44 ?2034次閱讀

    SystemVerilog中枚舉類型的使用建議

    SystemVerilog中枚舉類型雖然屬于一種“強類型”,但是枚舉類型還是提供了一些“不正經”的用法可以實現一些很常見的功能,本文將示例一
    的頭像 發表于 09-01 14:20 ?2026次閱讀

    淺析SystemVerilog中的枚舉類型

    枚舉類型定義了一組具有名稱的值,沒有指定值時默認是int型數值。
    的頭像 發表于 10-13 09:44 ?2016次閱讀

    枚舉類型的簡單擴展學習

    定義枚舉類型的值只能是整型常量,正數、負數和零都可以,但不可以是浮點數
    的頭像 發表于 05-25 15:45 ?781次閱讀

    接口中的有效數據類型和布局

    接口中包含有塊所用局部變量和局部常量的聲明。這些變量可分為以下兩組。
    的頭像 發表于 06-10 11:33 ?1240次閱讀
    塊<b class='flag-5'>接口中</b>的有效數據<b class='flag-5'>類型</b>和布局

    Java枚舉的特點及用法

    Java 枚舉出現之前,通常會使用常量類來表示一組固定的常量值,直到Java 1.5之后推出了枚舉,那么枚舉類型有哪些特點,它比常量類又
    的頭像 發表于 09-30 10:02 ?1861次閱讀

    java switch case值能為枚舉值嗎

    Java中的switch語句可以接受枚舉類型的值作為參數。Java中,枚舉是一種特殊的數據類型,它定義了一個固定數量的命名常量。因此,可以
    的頭像 發表于 11-30 14:41 ?6205次閱讀

    枚舉有多大?c語言枚舉end的作用是什么?

    枚舉有多大?c語言枚舉end的作用是什么? 枚舉C語言中是一種常見的數據類型,用于定義一組相互關聯的常量或者變量。它通常用于表示一系列可能
    的頭像 發表于 01-19 14:19 ?864次閱讀

    看看人家都用樹莓派5做了什么產品?

    看看人家都用樹莓派5做了什么產品?
    的頭像 發表于 03-25 09:46 ?182次閱讀
    <b class='flag-5'>看看人家</b>都用樹莓派5做了什么產品?