国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

springboot學(xué)習(xí)(三)——使用HttpMessageConverter進行http序列化和反

stackfing / 1821人閱讀

摘要:序列化反序列化主要體現(xiàn)在程序這個過程中,包括網(wǎng)絡(luò)和磁盤。如果是開發(fā)應(yīng)用,一般這兩個注解對應(yīng)的就是序列化和反序列化的操作。協(xié)議的處理過程,字節(jié)流內(nèi)部對象,就涉及這兩種序列化。進行第二步操作,也就是序列化和反序列化的核心是。

以下內(nèi)容,如有問題,煩請指出,謝謝!

對象的序列化/反序列化大家應(yīng)該都比較熟悉:序列化就是將object轉(zhuǎn)化為可以傳輸?shù)亩M制,反序列化就是將二進制轉(zhuǎn)化為程序內(nèi)部的對象。序列化/反序列化主要體現(xiàn)在程序I/O這個過程中,包括網(wǎng)絡(luò)I/O和磁盤I/O。

那么什么是http序列化和反序列化呢?

在使用springmvc時,我們經(jīng)常會這樣寫:

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User getUserById(@PathVariable long id) {
        return userService.getUserById(id);
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        System.err.println("create an user: " + user);
        return user;
    }
}

@RestController中有@ResponseBody,可以幫我們把User序列化到resp.body中。@RequestBody可以幫我們把req.body的內(nèi)容轉(zhuǎn)化為User對象。如果是開發(fā)Web應(yīng)用,一般這兩個注解對應(yīng)的就是Json序列化和反序列化的操作。這里實際上已經(jīng)體現(xiàn)了Http序列化/反序列化這個過程,只不過和普通的對象序列化有些不一樣,Http序列化/反序列化的層次更高,屬于一種Object2Object之間的轉(zhuǎn)換。

有過Netty使用經(jīng)驗的對這個應(yīng)該比較了解,Netty中的Decoder和Encoder就有兩種基本層次,層次低的一種是Byte <---> Message,二進制與程序內(nèi)部消息對象之間的轉(zhuǎn)換,就是常見的序列化/反序列化;另外一種是 Message <---> Message,程序內(nèi)部對象之間的轉(zhuǎn)換,比較高層次的序列化/反序列化。

Http協(xié)議的處理過程,TCP字節(jié)流 <---> HttpRequest/HttpResponse <---> 內(nèi)部對象,就涉及這兩種序列化。在springmvc中第一步已經(jīng)由Servlet容器(tomcat等等)幫我們處理了,第二步則主要由框架幫我們處理。上面所說的Http序列化/反序列化就是指的這第二個步驟,它是controller層框架的核心功能之一,有了這個功能,就能大大減少代碼量,讓controller的邏輯更簡潔清晰,就像上面示意的代碼那樣,方法中只有一行代碼。

spirngmvc進行第二步操作,也就是Http序列化和反序列化的核心是HttpMessageConverter。用過老版本springmvc的可能有些印象,那時候需要在xml配置文件中注入MappingJackson2HttpMessageConverter這個類型的bean,告訴springmvc我們需要進行Json格式的轉(zhuǎn)換,它就是HttpMessageConverter的一種實現(xiàn)。

在Web開發(fā)中我們經(jīng)常使用Json相關(guān)框架來進行第二步操作,這是因為Web應(yīng)用中主要開發(fā)語言是js,對Json支持非常好。但是Json也有很大的缺點,大多數(shù)Json框架對循環(huán)引用支持不夠好,并且Json報文體積通常比較大,相比一些二進制序列化更耗費流量。很多移動應(yīng)用也使用Http進行通信,因為這是在手機app中,Json格式報文并沒有什么特別的優(yōu)勢。這種情況下我們可能會需要一些性能更好,體積更小的序列化框架,比如Protobuf等等。

當(dāng)前的SpringMVC 4.3版本已經(jīng)集成了Protobuf的Converter,org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter,使用這個類可以進行Protobuf中的Message類和http報文之間的轉(zhuǎn)換。使用方式很簡單,先依賴Protobuf相關(guān)的jar,代碼中直接@Bean就行,像下面這樣,springboot會自動注入并添加這種Converter。

    @Bean
    public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }

這里就不演示protobuf相關(guān)的內(nèi)容了。

另外有很重要的一點需要說明一下,springmvc可以同時配置多個Converter,根據(jù)一定的規(guī)則(主要是Content-Type、Accept、controller方法的consumes/produces、Converter.mediaType以及Converter的排列順序這四個屬性)來選擇到底是使用哪一個,這使得springmvc能夠一個接口支持多種報文格式。這個規(guī)則的具體內(nèi)容,下一篇再詳細(xì)說明。

下面重點說下如何自定義一個HttpMessageConverter,就用Java原生序列化為例,叫作JavaSerializationConverter,基本仿照ProtobufHttpMessageConverter來寫。

首先繼承AbstractHttpMessageConverter,泛型類這里有幾種方式可以選擇:

最廣的可以選擇Object,不過Object并不都是可以序列化的,但是可以在覆蓋的supports方法中進一步控制,因此選擇Object是可以的

最符合的是Serializable,既完美滿足泛型定義,本身也是個Java序列化/反序列化的充要條件

自定義的基類Bean,有些技術(shù)規(guī)范要求自己代碼中的所有bean都繼承自同一個自定義的基類BaseBean,這樣可以在Serializable的基礎(chǔ)上再進一步控制,滿足自己的業(yè)務(wù)要求

這里選擇Serializable作為泛型基類。

其次是選擇一個MediaType,使得springmvc能夠根據(jù)Accept和Content-Type唯一確定是要使用JavaSerializationConverter,所以這個MediaType不能是通用的text/plain、application/json、*/*這種,得特殊一點,這里就用application/x-java-serialization;charset=UTF-8。因為Java序列化是二進制數(shù)據(jù),charset不是必須的,但是MediaType的構(gòu)造方法中需要指定一個charset,這里就用UTF-8。

最后,二進制在電腦上不是可以直接拷貝的內(nèi)容,為了方便測試,使用Base64再處理一遍,這樣就顯示成正常文本了,便于測試。

整個代碼如下:

package pr.study.springboot.configure.mvc.converter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Base64;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;

public class JavaSerializationConverter extends AbstractHttpMessageConverter {
    private Logger LOGGER = LoggerFactory.getLogger(JavaSerializationConverter.class);

    public JavaSerializationConverter() {
        // 構(gòu)造方法中指明consumes(req)和produces(resp)的類型,指明這個類型才會使用這個converter
        super(new MediaType("application", "x-java-serialization", Charset.forName("UTF-8")));
    }

    @Override
    protected boolean supports(Class clazz) {
        // 使用Serializable,這里可以直接返回true
        // 使用object,這里還要加上Serializable接口實現(xiàn)類判斷
        // 根據(jù)自己的業(yè)務(wù)需求加上其他判斷
        return true;
    }

    @Override
    protected Serializable readInternal(Class clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        byte[] bytes = StreamUtils.copyToByteArray(inputMessage.getBody());
        // base64使得二進制數(shù)據(jù)可視化,便于測試
        ByteArrayInputStream bytesInput = new ByteArrayInputStream(Base64.getDecoder().decode(bytes));
        ObjectInputStream objectInput = new ObjectInputStream(bytesInput);
        try {
            return (Serializable) objectInput.readObject();
        } catch (ClassNotFoundException e) {
            LOGGER.error("exception when java deserialize, the input is:{}", new String(bytes, "UTF-8"), e);
            return null;
        }
    }

    @Override
    protected void writeInternal(Serializable t, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        ByteArrayOutputStream bytesOutput = new ByteArrayOutputStream();
        ObjectOutputStream objectOutput = new ObjectOutputStream(bytesOutput);
        objectOutput.writeObject(t);
        // base64使得二進制數(shù)據(jù)可視化,便于測試
        outputMessage.getBody().write(Base64.getEncoder().encode(bytesOutput.toByteArray()));
    }

}

添加一個converter的方式有三種,代碼以及說明如下:

    // 添加converter的第一種方式,代碼很簡單,也是推薦的方式
    // 這樣做springboot會把我們自定義的converter放在順序上的最高優(yōu)先級(List的頭部)
    // 即有多個converter都滿足Accpet/ContentType/MediaType的規(guī)則時,優(yōu)先使用我們這個
    @Bean
    public JavaSerializationConverter javaSerializationConverter() {
        return new JavaSerializationConverter();
    }

    // 添加converter的第二種方式
    // 通常在只有一個自定義WebMvcConfigurerAdapter時,會把這個方法里面添加的converter(s)依次放在最高優(yōu)先級(List的頭部)
    // 雖然第一種方式的代碼先執(zhí)行,但是bean的添加比這種方式晚,所以方式二的優(yōu)先級 大于 方式一
    @Override
    public void configureMessageConverters(List> converters) {
        // add方法可以指定順序,有多個自定義的WebMvcConfigurerAdapter時,可以改變相互之間的順序
        // 但是都在springmvc內(nèi)置的converter前面
        converters.add(new JavaSerializationConverter());
    }

    // 添加converter的第三種方式
    // 同一個WebMvcConfigurerAdapter中的configureMessageConverters方法先于extendMessageConverters方法執(zhí)行
    // 可以理解為是三種方式中最后執(zhí)行的一種,不過這里可以通過add指定順序來調(diào)整優(yōu)先級,也可以使用remove/clear來刪除converter,功能強大
    // 使用converters.add(xxx)會放在最低優(yōu)先級(List的尾部)
    // 使用converters.add(0,xxx)會放在最高優(yōu)先級(List的頭部)
    @Override
    public void extendMessageConverters(List> converters) {
        converters.add(new JavaSerializationConverter());
    }

使用下面的數(shù)據(jù)演示:

// java序列化
rO0ABXNyAB1wci5zdHVkeS5zcHJpbmdib290LmJlYW4uVXNlcrt1879rvWjlAgAESgACaWRMAApjcmVhdGVUaW1ldAAQTGphdmEvdXRpbC9EYXRlO0wABWVtYWlsdAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgACeHIAIXByLnN0dWR5LnNwcmluZ2Jvb3QuYmVhbi5CYXNlQmVhbklx6Fsr8RKpAgAAeHAAAAAAAAAAe3NyAA5qYXZhLnV0aWwuRGF0ZWhqgQFLWXQZAwAAeHB3CAAAAWCs8ufxeHQAEGhlbGxvd29ybGRAZy5jb210AApoZWxsb3dvcmxk

// json
{"id":123,"name":"helloworld","email":"helloworld@g.com","createTime":"2017-12-31 22:21:28"}

// 對應(yīng)的user.toString()
User[id=123, name=helloworld, email=helloworld@g.com, createTime=Sun Dec 31 22:21:28 CST 2017]

演示結(jié)果如下,包含了一個接口多種報文格式支持的演示:

1、請求是 GET + Accept: application/x-java-serialization,返回的是 Content-Type: application/x-java-serialization;charset=UTF-8 的Java序列化格式的報文

2、請求是 GET + Accept: application/json,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報文

3、請求是 POST + Accept: application/x-java-serialization + Content-Type: application/x-java-serialization,返回的是 Content-Type: application/x-java-serialization;charset=UTF-8的Java序列化格式的報文

4、請求是 POST + Accept: application/json + Content-Type: application/x-java-serialization,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報文

5、請求是 POST + Accept: application/json + Content-Type: application/json,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報文

6、請求是 POST + Accept: application/x-java-serialization + Content-Type: application/json,返回的是 Content-Type: application/x-java-serialization;charset=UTF-8的Java序列化格式的報文

下面再說些其他的有關(guān)Http序列化/反序列化的內(nèi)容.
1、jackson配置
使用Jackson時,一般我們都會配置下ObjectMapper,常見的兩個是時間序列化格式,以及是否序列化null值。使用springboot時,因為Jackson是內(nèi)置加載的,那么如何配置我們想要的的Jackson屬性呢?最賤的的方式,那就是自己注入一個ObjectMapper實例,這樣spring內(nèi)所有通過依賴注入使用ObjectMapper的地方,都會優(yōu)先使用我們自己注入的那個,JacksonConverter也不例外。

/**
 * jackson的核心是ObjectMapper,在這里配置ObjectMapper來控制springboot使用的jackson的某些功能
 */
@Configuration
public class MyObjectMpper {

    @Bean
    public ObjectMapper getObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_NULL); // 不序列化null的屬性
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 默認(rèn)的時間序列化格式
        return mapper;
    }
}

2、控制Json中某些屬性的序列化方式
官方文檔中說了個Custom JSON Serializers and Deserializers,我也沒想到怎么用這個,后來網(wǎng)上發(fā)現(xiàn)了個比較好的例子,說的是rgb顏色的序列化。web頁面需要的是css格式的rgb顏色,服務(wù)的提供的可能是三個獨立的byte型數(shù)字,這時候就需要改變顏色屬性的json序列化/反序列化方式。具體可以看看這里。

3、FastJson配置
可能某些時候需要使用FastJson,這時候該如何配置呢?基本上和springmvc xml配置差不多,注入一個FastJsonHttpMessageConverter就行了。最簡單的就是上面的配置JavaSerializationConverter的方式一,方式二和方式三也都行。
不過會有奇怪的問題出現(xiàn)(使用@JSONField(serialize=false, deserialize=false)注解createTime,用以區(qū)分FastJson和Jackson):
假如你把FastJson配置為優(yōu)先級最高的,并且同時配置上JavaSerializationConverter,你會發(fā)現(xiàn)JavaSerializationConverter不管用了,請求是 GET + Accept: application/x-java-serialization,返回是 Content-Type: application/x-java-serialization;charset=UTF-8;,但是實際內(nèi)容是json格式的,如下。

假如你把FastJson配置為優(yōu)先級最低的,別的不管,你以為得到會是Jackson序列化后的結(jié)果。但實際上,你用瀏覽器直接敲得到的是FastJson的,用上面的 GET 的 fiddler結(jié)果是jackson的;

詳細(xì)原因在下一篇講解converter匹配規(guī)則時說。

這里說下原因中重要且值得吐槽的一點,那就是FastJsonHttpMessageConverter默認(rèn)注冊的MediaType的 */*,然后就有了上面的 請求是 GET + Accept: application/x-java-serialization,返回是 Content-Type: application/x-java-serialization;charset=UTF-8;,但是實際內(nèi)容是json格式的,這種掛羊頭賣狗肉的行為,明著違反HTTP協(xié)議的規(guī)范。

這個代碼設(shè)計真是差,json框架就該只管json,這樣霸道,什么格式都要管,為哪般???

相關(guān)代碼:
https://gitee.com/page12/stud...
https://github.com/page12/stu...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68126.html

相關(guān)文章

  • SpringMVC HttpMessageConverter 匹配規(guī)則

    摘要:例如,服務(wù)端不支持這種,應(yīng)該返回。而當(dāng)使用或者其他等價方式進行配置時,會把添加在最前面,優(yōu)先級最高。好了,到此就基本上說完了整個的匹配規(guī)則或者叫選擇過程。自己能力不是最大,卻大包大攬承擔(dān)最大責(zé)任,處理不了還返回,是甩鍋客戶端的行為。 以下內(nèi)容,如有問題,煩請指出,謝謝! SpringMVC啟動時會自動配置一些HttpMessageConverter,接收到http請求時,從這些Con...

    Imfan 評論0 收藏0
  • Spring Boot 參考指南(開發(fā)Web應(yīng)用程序)

    摘要:開發(fā)應(yīng)用程序非常適合應(yīng)用程序開發(fā),通過使用嵌入式或,你可以創(chuàng)建一個自包含的服務(wù)器。如果你還沒有開發(fā)過一個應(yīng)用程序,你可以按照入門部分中的示例進行操作。自動配置為大多數(shù)應(yīng)用程序提供了良好的自動配置。 27. 開發(fā)Web應(yīng)用程序 Spring Boot非常適合web應(yīng)用程序開發(fā),通過使用嵌入式Tomcat、Jetty、Undertow或Netty,你可以創(chuàng)建一個自包含的HTTP服務(wù)器。大多...

    roadtogeek 評論0 收藏0
  • 《 Kotlin + Spring Boot : 下一代 Java 服務(wù)端開發(fā) 》

    摘要:下一代服務(wù)端開發(fā)下一代服務(wù)端開發(fā)第部門快速開始第章快速開始環(huán)境準(zhǔn)備,,快速上手實現(xiàn)一個第章企業(yè)級服務(wù)開發(fā)從到語言的缺點發(fā)展歷程的缺點為什么是產(chǎn)生的背景解決了哪些問題為什么是的發(fā)展歷程容器的配置地獄是什么從到下一代企業(yè)級服務(wù)開發(fā)在移動開發(fā)領(lǐng)域 《 Kotlin + Spring Boot : 下一代 Java 服務(wù)端開發(fā) 》 Kotlin + Spring Boot : 下一代 Java...

    springDevBird 評論0 收藏0
  • PHP反列化漏洞系列之–PHP列化和反列化原理

    0.前言 本文為篤行日常學(xué)習(xí)記錄,web安全php漏洞系列。 對象的序列化和反序列化作用就不再贅述,php中序列化的結(jié)果是一個php自定義的字符串格式,有點類似json. 我們在任何語言中設(shè)計對象的序列化和反序列化都需要解決幾個問題 把某個對象序列化之后,序列化的結(jié)果有自描述的功能(從序列化的結(jié)果中知道這個對象的具體類型,知道類型還不夠,當(dāng)然還需要知道這個類型所對應(yīng)具體的值). 序列化時的權(quán)...

    caozhijian 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<