<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot 使用轉(zhuǎn)換器將前端參數(shù)轉(zhuǎn)換為枚舉

          共 11290字,需瀏覽 23分鐘

           ·

          2021-09-05 18:57

          前言

          最近遇到一個(gè)小伙伴問前端枚舉轉(zhuǎn)換問題,才意識到可以通過轉(zhuǎn)換器(Converter)自動將前端傳入的字段值使用枚舉接收。

          我自己搗鼓了一番,現(xiàn)在記錄筆記分享一下!有興趣的小伙伴可以自己嘗試一下!

          這里使用的是 MyBatis-Plus 和 SpringBoot 2.3.4.RELEASE

          1

          實(shí)現(xiàn)過程

          配置轉(zhuǎn)換器

          /**
           * @author liuzhihang
           * @date 2021/8/31 16:29
           */

          @Configuration
          public class WebConfig implements WebMvcConfigurer {

              @Override
              public void addFormatters(FormatterRegistry registry) {

                  registry.addConverterFactory(new ConverterFactory<Object, BaseEnum>() {
                      @Override
                      public <T extends BaseEnum> Converter<Object, T> getConverter(Class<T> targetType) {

                          T[] enums = targetType.getEnumConstants();

                          return source -> {

                              for (T e : enums) {
                                  if (e.getCode().equals(source)) {
                                      return e;
                                  }
                              }

                              throw new IllegalArgumentException("枚舉 Code 不正確");
                          };
                      }
                  });
              }
          }

          直接在 WebMvcConfigurer 里實(shí)現(xiàn) addFormatters 方法即可,然后 new 一個(gè) ConverterFactory。

          WebMvcConfigurer 相信大家都不陌生,一般添加一些攔截器,通用校驗(yàn) token、日志等等都會用到。具體可以參考這篇文章:幾行代碼輕松實(shí)現(xiàn)跨系統(tǒng)傳遞 traceId,再也不用擔(dān)心對不上日志了!,里面有一些其他的應(yīng)用。

          就這些,很簡單的實(shí)現(xiàn)。下面介紹下項(xiàng)目的內(nèi)容和代碼,方便理解。

          項(xiàng)目代碼

          • 請求參數(shù):
          POST http://localhost:8818/user/listByStatus
          Content-Type: application/json

          {
          "orderStatus": 1
          }
          • Controller
          /**
           * @author liuzhihang
           * @date 2021/8/30 11:08
           */

          @Slf4j
          @RestController
          @RequestMapping("/user")
          public class UserController {

              @Autowired
              private OrderService orderService;

              @PostMapping(value = "/listByStatus")
              public ResultVO<UserResponse> listByStatus(@Validated @RequestBody UserRequest request)  {

                  log.info("請求參數(shù):{}", request);

                  List<TransOrder> orderList = orderService.getByOrderStatus(request.getOrderStatus());

                  UserResponse response = new UserResponse();

                  response.setRecords(orderList);

                  log.info("返回參數(shù):{}", response);

                  return ResultVO.success(response);
              }
          }
          • Entity
          @Data
          public class UserRequest {

              private OrderStatusEnum orderStatus;
              private ViewStatusEnum viewStatus;
          }

          @Data
          public class UserResponse {

              private List<TransOrder> records;

          }

          Web 傳入 orderStatus 為 1,而后端接收對象是 UserRequest 的 orderStatus 字段是個(gè) OrderStatusEnum 類型的枚舉。

          這里就需要自動將數(shù)字類型的字段轉(zhuǎn)換為枚舉字段。這個(gè)枚舉會直接通過 MyBatis-Plus 查詢。

          為什么要這么用呢?

          其實(shí)原因很簡單,使用枚舉限制數(shù)據(jù)庫字段的類型,比如數(shù)據(jù)庫狀態(tài)只有 0、1、2,那就和代碼里的枚舉對應(yīng)起來。防止傳入其他值。

          • 枚舉
          public interface BaseEnum {
              Object getCode();
          }
          public enum OrderStatusEnum implements BaseEnum {

              INIT(0"初始狀態(tài)"),
              SUCCESS(1"成功"),
              FAIL(2"失敗");

              @EnumValue
              @JsonValue
              private final int code;

              private final String desc;

              OrderStatusEnum(int code, String desc) {
                  this.code = code;
                  this.desc = desc;
              }

              @Override
              public Integer getCode() {
                  return code;
              }

              public String getDesc() {
                  return desc;
              }
          }

          這里先聲明接口 BaseEnum,所有的枚舉都繼承這個(gè)接口,并實(shí)現(xiàn) getCode 方法。

          @EnumValue:MyBatis-Plus 的枚舉,和數(shù)據(jù)庫字段映射用的

          @JsonValue:返回給前端時(shí),這個(gè)枚舉字段序列化時(shí),返回參數(shù)只顯示 code。

          這樣就可以實(shí)現(xiàn)效果,請求參數(shù)為數(shù)字,接收對象字段為枚舉,返回字段也是 code。

          效果

          測試結(jié)果

          測試結(jié)果經(jīng)過驗(yàn)證,是可以勝任傳入數(shù)值和字符串的。

          也可以結(jié)合異常處理器,返回通用異常。具體怎么用查一查 @ExceptionHandler 就知道了。

          具體說明

          在 addFormatters 方法中可以看到 registry.addConverterFactory() 接收的是一個(gè) ConverterFactory 對象。

          public interface ConverterFactory<SR{

           <T extends R> Converter<S, T> getConverter(Class<T> targetType);
          }
          1. S 就是傳入的字段類型(數(shù)字,字符串)
          2. R 是要轉(zhuǎn)換為的類型(枚舉)
          3. T 繼承了 R,其實(shí)就是參數(shù)對象中字段的類型

          在 ConverterFactory 的 getConverter 方法則需要返回一個(gè)實(shí)際的轉(zhuǎn)換器 Converter

          @FunctionalInterface
          public interface Converter<ST{

           @Nullable
           convert(S source);

          }

          convert 方法的入?yún)⑹且粋€(gè) source,就是要轉(zhuǎn)換為什么類型的,這里就是數(shù)字/字符串,然后返回一個(gè)枚舉即可。

          注意這里加了 @FunctionalInterface 就意味著這里是可以用 lambda 表達(dá)式的。

          2

          優(yōu)化

          一般 WebConfig 中除了實(shí)現(xiàn) addFormatters 方法外,還會實(shí)現(xiàn) addInterceptors 等等,這樣寫難免會很長,所以可以改為下面這種。

          @Configuration
          public class WebConfig implements WebMvcConfigurer {

              @Autowired
              private LogInterceptor logInterceptor;

              @Autowired
              private AppTokenInterceptor appTokenInterceptor;


              @Autowired
              private EnumConverterFactory enumConverterFactory;

              @Override
              public void addInterceptors(InterceptorRegistry registry) {

                  // 日志
                  registry.addInterceptor(logInterceptor)
                          .addPathPatterns("/**");

                  // app token校驗(yàn)
                  registry.addInterceptor(appTokenInterceptor)
                          .addPathPatterns("/app/**");

              }

              @Override
              public void addFormatters(FormatterRegistry registry) {
                  
                  // 枚舉轉(zhuǎn)換
                  registry.addConverterFactory(enumConverterFactory);
              }
          }

          這種就需要咱們創(chuàng)建 EnumConverterFactory 類并實(shí)現(xiàn) ConverterFactory 接口了,還得注入到 Spring 容器中

          @Component
          public class EnumConverterFactory implements ConverterFactory<ObjectBaseEnum{

              @Override
              public <T extends BaseEnum> Converter<Object, T> getConverter(Class<T> targetType) {

                  T[] enums = targetType.getEnumConstants();

                  return source -> {
                      for (T e : enums) {
                          if (e.getCode().equals(source)) {
                              return e;
                          }
                      }

                      throw new IllegalArgumentException("枚舉 Code 不正確");
                  };
              }
          }

          要是實(shí)在覺得 lambda 看不慣,并且也不夠優(yōu)雅,那可以使用下面的方式。

          @Component
          public class EnumConverterFactory implements ConverterFactory<ObjectBaseEnum{

              @Override
              public <T extends BaseEnum> Converter<Object, T> getConverter(Class<T> targetType) {

                  return new EnumConverter<>(targetType);
              }
          }
          public class EnumConverter<T extends BaseEnumimplements Converter<ObjectT{

              private final Class<T> targetType;

              public EnumConverter(Class<T> targetType) {
                  this.targetType = targetType;
              }

              @Override
              public T convert(Object source) {

                  for (T e : targetType.getEnumConstants()) {
                      if (e.getCode().equals(source)) {
                          return e;
                      }
                  }

                  throw new IllegalArgumentException("枚舉 Code 不正確");
              }
          }

          3

          總結(jié)

          當(dāng)然這里也有一些其他的優(yōu)化點(diǎn),比如可以使用緩存將 Convert 緩存起來。

          不過我也遇到一個(gè)其他的問題,就是我 debug 斷點(diǎn)竟然一直沒有斷到轉(zhuǎn)換器中,不知道有沒有小伙伴嘗試過?


          - <End /> -




          歷史文章 | 相關(guān)推薦



          瀏覽 121
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色情五月 | 成人做爱视频在线观看免费版网站 | 黄色特一级黄片 | 理论亚洲成人电影 | 欧美一级欧美三级在线观看 |