国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

詳解Spring自定義消息格式轉(zhuǎn)換器及底層源碼分析

開發(fā) 前端
在配置消息轉(zhuǎn)換器時(shí),指明了當(dāng)前這個(gè)消息轉(zhuǎn)換器能夠接收的內(nèi)容類型,也就是客戶端請(qǐng)求時(shí)需要設(shè)定Content-Type為application/fm。

假設(shè)現(xiàn)在要實(shí)現(xiàn)這樣的一個(gè)消息格式:

入?yún)ⅲ?/p>

name:張三,age:20

圖片圖片

接口接收對(duì)象Users

自定義消息轉(zhuǎn)換器

public class CustomHttpMessageConverter extends AbstractHttpMessageConverter<Object> {


  private static Logger logger = LoggerFactory.getLogger(CustomHttpMessageConverter.class) ;
  
  // 這里指明了只要接收參數(shù)是Users類型的都能進(jìn)行轉(zhuǎn)換
  @Override
  protected boolean supports(Class<?> clazz) {
    return Users.class == clazz ;
  }
  // 讀取內(nèi)容進(jìn)行內(nèi)容的轉(zhuǎn)換
  @Override
  protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
      throws IOException, HttpMessageNotReadableException {
    String content = inToString(inputMessage.getBody()) ;
    String[] keys = content.split(",") ;
    Users instance = null ;
    try {
      instance = (Users) clazz.newInstance();
    } catch (Exception e1) {
      e1.printStackTrace() ;
    }
    for (String key : keys) {
      String[] vk = key.split(":") ;
      try {
        Field[] fields = clazz.getDeclaredFields() ;
        for (Field f:fields) {
          if (f.getName().equals(vk[0])) {
            f.setAccessible(true) ;
            Class<?> type = f.getType() ;
            if (String.class == type) {
              f.set(instance, vk[1]) ;
            } else if (Integer.class == type) {
              f.set(instance, Integer.parseInt(vk[1])) ;
            }
            break ;
          }
        }
      } catch (Exception e) {
        logger.error("錯(cuò)誤:{}", e) ;
      }
    }
    return instance ;
  }


  // 如果將返回值以什么形式輸出,這里就是調(diào)用了對(duì)象的toString方法。
  @Override
  protected void writeInternal(Object t, HttpOutputMessage outputMessage)
      throws IOException, HttpMessageNotWritableException {
    outputMessage.getBody().write(t.toString().getBytes()) ;
  }
  
  @Override
  protected boolean canWrite(MediaType mediaType) {
    if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
      return true;
    }
    for (MediaType supportedMediaType : getSupportedMediaTypes()) {
      if (supportedMediaType.isCompatibleWith(mediaType)) {
        return true;
      }
    }
    return false;
  }
  
  private String inToString(InputStream is) {
    byte[] buf = new byte[10 * 1024] ;
    int leng = -1 ;
    StringBuilder sb = new StringBuilder() ;
    try {
      while ((leng = is.read(buf)) != -1) {
        sb.append(new String(buf, 0, leng)) ;
      }
      return sb.toString() ;
    } catch (IOException e) {
      throw new RuntimeException(e) ;
    }
  }


}

配置消息轉(zhuǎn)換器

@Configuration
public class WebConfig implements WebMvcConfigurer {


  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    CustomHttpMessageConverter messageConvert = new CustomHttpMessageConverter() ;
    List<MediaType> supportedMediaTypes = new ArrayList<>() ;
    supportedMediaTypes.add(new MediaType("application", "fm")) ;
    messageConvert.setSupportedMediaTypes(supportedMediaTypes) ;
    converters.add(messageConvert) ;
    WebMvcConfigurer.super.configureMessageConverters(converters);
  }
  
}

在配置消息轉(zhuǎn)換器時(shí),指明了當(dāng)前這個(gè)消息轉(zhuǎn)換器能夠接收的內(nèi)容類型,也就是客戶端請(qǐng)求時(shí)需要設(shè)定Content-Type為application/fm。

參數(shù)對(duì)象

public class Users {
  
  private String name ;
  private Integer age ;


  
  @Override
  public String toString() {
    return "【name = " + this.name + ", age = " + this.age + "】" ;
  }
  
}

Controller接口

@RestController
@RequestMapping("/message")
public class MessageController {
  
  @PostMapping("/save")
  public Users save(@RequestBody Users user) {
    System.out.println("接受到內(nèi)容:" + user) ;
    return user ;
  }
}

測(cè)試

請(qǐng)求:

圖片圖片

圖片圖片


響應(yīng)

圖片圖片


圖片圖片


源碼分析為何自定義消息轉(zhuǎn)換器時(shí)要重寫那幾個(gè)方法:

由于我們的接口參數(shù)用@RequestBody 注解了,系統(tǒng)采用了

RequestResponseBodyMethodProcessor這個(gè)參數(shù)解析器進(jìn)行參數(shù)的處理。

整個(gè)處理流程的入口是DispatcherServlet中的這行代碼:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

接著進(jìn)入RequestMappingHandlerAdapter#handleInternal方法中的這行代碼:

mav = invokeHandlerMethod(request, response, handlerMethod);

接著進(jìn)入RequestMappingHandlerAdapter#invokeHandlerMethod方法的這行代碼:

invocableMethod.invokeAndHandle(webRequest, mavContainer);

接著進(jìn)入ServletInvocableHandlerMethod#invokeAndHandle方法中的這行代碼:

Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);

接著進(jìn)入invokeForRequest方法

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    Object... providedArgs) throws Exception {


  Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  if (logger.isTraceEnabled()) {
    logger.trace("Arguments: " + Arrays.toString(args));
  }
  return doInvoke(args);
}

接著進(jìn)入getMethodArgumentValues方法

圖片圖片

1、這里就開始判斷有沒有參數(shù)解析器可以處理,如果沒有會(huì)拋出異常。

這里還會(huì)吧找到處理的參數(shù)解析器緩存起來

圖片圖片

this.argumentResolverCache.put(parameter, result);

這行代碼緩存了當(dāng)前可以處理的解析器。

2、開始解析參數(shù),直接從緩存中獲取。因?yàn)樯弦徊揭呀?jīng)得到了解析器。

圖片圖片

得到了解析器后:

圖片圖片

進(jìn)行入選中的方法,這個(gè)方法最終會(huì)進(jìn)入父類
AbstractMessageConverterMethodArgumentResolver的如下方法:

protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
    Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {


  MediaType contentType;
  boolean noContentType = false;
  try {
    contentType = inputMessage.getHeaders().getContentType();
  }
  catch (InvalidMediaTypeException ex) {
    throw new HttpMediaTypeNotSupportedException(ex.getMessage());
  }
  if (contentType == null) {
    noContentType = true;
    contentType = MediaType.APPLICATION_OCTET_STREAM;
  }


  Class<?> contextClass = parameter.getContainingClass();
  Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
  if (targetClass == null) {
    ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
    targetClass = (Class<T>) resolvableType.resolve();
  }


  HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
  Object body = NO_VALUE;


  EmptyBodyCheckingHttpInputMessage message;
  try {
    message = new EmptyBodyCheckingHttpInputMessage(inputMessage);


    for (HttpMessageConverter<?> converter : this.messageConverters) {
      Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
      GenericHttpMessageConverter<?> genericConverter =
          (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
      if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
          (targetClass != null && converter.canRead(targetClass, contentType))) {
        if (message.hasBody()) {
          HttpInputMessage msgToUse =
              getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
          body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
              ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
          body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
        }
        else {
          body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
        }
        break;
      }
    }
  }
  catch (IOException ex) {
    throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
  }


  if (body == NO_VALUE) {
    if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
        (noContentType && !message.hasBody())) {
      return null;
    }
    throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
  }


  MediaType selectedContentType = contentType;
  Object theBody = body;
  LogFormatUtils.traceDebug(logger, traceOn -> {
    String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
    return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
  });


  return body;
}

該方法中的this.messageConverters數(shù)據(jù)如下:

圖片圖片

這里可以看到我們自定義的CustomHttpMessageConverter。

繼續(xù)調(diào)試到我們自定義的這個(gè)Converter

圖片圖片

從這里看出,會(huì)執(zhí)行 else(:)中的代碼

targetClass != null && converter.canRead(targetClass, contentType)

這個(gè)canRead是父類中的方法:

圖片圖片

support這里就進(jìn)入到了我們自定義的Converter中。

圖片圖片

繼續(xù)就會(huì)進(jìn)入到read方法,真正讀取處理消息內(nèi)容的代碼了

圖片圖片

這里的readInternal就是我們自定義的方法了

圖片圖片


關(guān)于write的相關(guān)方法和read差不多,也就是判斷能否write,然后調(diào)用對(duì)應(yīng)的writeInternal方法。


責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2022-06-20 08:26:39

Spring容器類型轉(zhuǎn)換

2011-03-17 09:45:01

Spring

2024-10-14 17:18:27

2023-06-06 08:01:18

自定義接口響應(yīng)

2025-05-12 08:00:55

2011-07-05 18:51:51

QT 控件 鼠標(biāo)

2010-02-25 11:23:29

WCF返回自定義格式

2011-07-04 14:08:02

C++

2009-12-28 13:38:35

WPF類型轉(zhuǎn)換器

2022-06-30 14:02:07

鴻蒙開發(fā)消息彈窗組件

2022-03-07 10:05:02

SpringStreamMQ連接

2010-03-02 18:01:07

WCF自定義消息篩選器

2019-10-16 16:33:41

Docker架構(gòu)語言

2015-02-11 17:49:35

Android源碼自定義控件

2021-03-15 08:34:26

FormatterSpringFormatterRe

2011-06-20 16:03:03

Qt 控件 鼠標(biāo)

2011-08-02 11:17:13

iOS開發(fā) View

2009-08-12 14:53:50

C#類型轉(zhuǎn)換函數(shù)

2013-01-14 11:40:50

IBMdW

2015-01-22 15:59:07

Android源碼日期時(shí)間控件SelectTime
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

午夜欧美激情| 欧美人与物videos另类| 91免费国产精品| 美女久久久久| 亚洲国产精品久久| 麻豆精品视频在线观看| 九九热视频这里只有精品| 一级毛片在线看| 国内不卡的二区三区中文字幕| 久久99亚洲精品| 欧美日韩视频在线播放| 亚洲福利一区二区三区| 91传媒免费视频| 久久亚洲欧美| 国产精品三区在线| 国产欧美日韩影院| 久久亚洲一区二区三区四区五区高| 五月婷婷在线视频| 一区二区三区日韩精品视频| 日日橹狠狠爱欧美超碰| 激情久久综合| 国产一区二区视频在线免费观看 | 国产资源在线看| 亚洲欧美综合色| 一级性生活视频| 欧美日韩成人| 欧美系列在线观看| 久久人人爽人人爽人人片av高请| 欧美精品日韩少妇| 欧美一区二区三区成人| 黄色精品免费看| 日韩视频一区二区三区在线播放| 欧美一区二区视频在线观看2020 | 97国产一区二区| 精品无码一区二区三区爱欲| 成人精品视频一区二区三区尤物| xf在线a精品一区二区视频网站| 成人白浆超碰人人人人| 在线精品视频一区| 亚洲精品一区二区三区中文字幕 | 一区二区三区四区五区在线 | 尤物在线网址| 999视频精品| 偷拍日韩校园综合在线| 成人字幕网zmw| 日本中文字幕网址| 欧美日韩中字| av片在线观看永久免费| 精品国产乱码久久久久久图片| 九色视频网站| 国产精品久久久久久亚洲伦| 樱花www成人免费视频| 成av人片在线观看www| 久久精品30| 欧美激情区在线播放| a免费在线观看| 欧美一级高清大全免费观看| h片视频在线观看| 夜夜亚洲天天久久| 色综合久久久久无码专区| 国产亚洲一区二区三区在线观看| 精品剧情v国产在线观看| 在线观看亚洲精品视频| av网站导航在线观看免费| 爽爽爽爽爽爽爽成人免费观看| 欧美亚洲色图校园春色| 狠狠色综合网站久久久久久久| 丰满人妻一区二区三区53号| 欧美一级网站| 精品无码av无码免费专区| 日韩制服丝袜av| 欧美福利精品| 成人一区二区在线观看| 老头吃奶性行交视频| 亚洲女同女同女同女同女同69| 黄色免费视频大全| 亚洲精品欧美专区| 蜜桃传媒在线观看免费进入 | 久久99久久精品国产| 五月婷婷色综合| 日本一区二区三区播放| 国产精品久久久久久久久久99| 国产精品亚洲а∨天堂免在线| 精品久久久噜噜噜噜久久图片 | 高清一区二区三区| 欧美日韩xxx| 国产精品流白浆在线观看| 欧美亚洲高清一区二区三区不卡| 神马久久影视大全| 日韩亚洲国产中文字幕欧美| 国产精品传媒麻豆hd| 国产有码在线一区二区视频| 国产成人精品aa毛片| 一级在线观看| 欧美成人精品一区二区三区| 亚洲一区二区三区免费在线观看| 色诱视频在线观看| 日韩午夜电影在线观看| 国产精品福利在线观看播放| 污污视频网站免费观看| 亚洲女人天堂网| 亚洲激情专区| 99久热re在线精彩视频| 福利视频在线播放| 国产拍揄自揄精品视频麻豆| h片在线播放| 日韩av日韩在线观看| 国产大陆精品国产| caopen在线视频| 国产精品极品在线| 久久综合九色综合97婷婷女人| 国产大片在线免费观看| 国产欧美综合一区| 美女主播视频一区| 亚洲国产裸拍裸体视频在线观看乱了中文 | gay视频丨vk| 亚洲国产精品字幕| 国产精品自产自拍| 少妇一晚三次一区二区三区| 色天使色偷偷av一区二区| 成人自拍在线| 奇米影视亚洲色图| 亚洲欧美另类中文字幕| 日本视频一区二区三区| shkd中文字幕久久在线观看| 亚洲iv一区二区三区| 亚洲国产美女搞黄色| 香蕉视频一区二区三区| 在线观看的毛片| www国产亚洲精品久久网站| 久久综合亚州| 精品日韩欧美在线| 国产欧美日韩在线一区二区| 视频一区二区在线播放| 欧美1—12sexvideos| 国产chinese精品一区二区| 疯狂做受xxxx高潮欧美日本| 精品一区二区三区在线| 日韩欧美中文字幕电影| 国产91精品高潮白浆喷水| 久久综合资源网| 国产91欧美| 成人在线观看你懂的| 久久视频在线看| 国产欧美日韩中文字幕| 日本老师69xxx| 中文字幕av不卡| 不卡视频观看| 一级黄色片播放| 中文字幕亚洲天堂| 波多野结衣一区二区三区| 图片一区二区| 99热.com| 国产在线精品自拍| 欧美日韩精品三区| 麻豆成人91精品二区三区| www.26天天久久天堂| 亚洲国产高清av| 成人信息集中地欧美| 欧美丰满嫩嫩电影| 国产一二三精品| 欧洲大片精品免费永久看nba| 天天操天天摸天天爽| 国产精品高清网站| 欧美精品在线观看播放| 国产福利电影一区二区三区| 国产精品一区二区中文字幕 | 日韩在线看片| 性xxxxfreexxxxx欧美丶| 2019中文字幕视频| 欧美亚洲激情在线| 在线不卡中文字幕| 99这里都是精品| 国产高清视频一区二区| 日韩精品免费一区| 欧美国产日韩一区二区| 一区二区在线电影| 国产精品亚洲产品| 向日葵视频成人app网址| 男人插女人欧美| 国产综合动作在线观看| 中文日韩电影网站| 精品免费在线观看| 麻豆一区二区99久久久久| www.久久东京| 欧美成人三区| 欧美国产日韩在线播放| 国产精品乱码视频| 久久久成人精品| 亚洲丶国产丶欧美一区二区三区| 日本不卡一区二区三区高清视频| 99国产精品久久一区二区三区| 香蕉视频在线看| 亚洲精品一二三四五区| 欧美成ee人免费视频| 久久久久久久久亚洲| 欧美一区在线视频| 成人免费在线视频| 国产主播一区二区三区| 99久久99久久精品国产片桃花|