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

資訊專欄INFORMATION COLUMN

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

stackfing / 1817人閱讀

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

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

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

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

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

@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對(duì)象。如果是開發(fā)Web應(yīng)用,一般這兩個(gè)注解對(duì)應(yīng)的就是Json序列化和反序列化的操作。這里實(shí)際上已經(jīng)體現(xiàn)了Http序列化/反序列化這個(gè)過程,只不過和普通的對(duì)象序列化有些不一樣,Http序列化/反序列化的層次更高,屬于一種Object2Object之間的轉(zhuǎn)換。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

整個(gè)代碼如下:

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)的類型,指明這個(gè)類型才會(huì)使用這個(gè)converter
        super(new MediaType("application", "x-java-serialization", Charset.forName("UTF-8")));
    }

    @Override
    protected boolean supports(Class clazz) {
        // 使用Serializable,這里可以直接返回true
        // 使用object,這里還要加上Serializable接口實(shí)現(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使得二進(jìn)制數(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使得二進(jìn)制數(shù)據(jù)可視化,便于測試
        outputMessage.getBody().write(Base64.getEncoder().encode(bytesOutput.toByteArray()));
    }

}

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

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

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

    // 添加converter的第三種方式
    // 同一個(gè)WebMvcConfigurerAdapter中的configureMessageConverters方法先于extendMessageConverters方法執(zhí)行
    // 可以理解為是三種方式中最后執(zhí)行的一種,不過這里可以通過add指定順序來調(diào)整優(yōu)先級(jí),也可以使用remove/clear來刪除converter,功能強(qiáng)大
    // 使用converters.add(xxx)會(huì)放在最低優(yōu)先級(jí)(List的尾部)
    // 使用converters.add(0,xxx)會(huì)放在最高優(yōu)先級(jí)(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"}

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

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

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

2、請(qǐng)求是 GET + Accept: application/json,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報(bào)文

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

4、請(qǐng)求是 POST + Accept: application/json + Content-Type: application/x-java-serialization,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報(bào)文

5、請(qǐng)求是 POST + Accept: application/json + Content-Type: application/json,返回的是 Content-Type: application/json;charset=UTF-8 的json格式報(bào)文

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

下面再說些其他的有關(guān)Http序列化/反序列化的內(nèi)容.
1、jackson配置
使用Jackson時(shí),一般我們都會(huì)配置下ObjectMapper,常見的兩個(gè)是時(shí)間序列化格式,以及是否序列化null值。使用springboot時(shí),因?yàn)镴ackson是內(nèi)置加載的,那么如何配置我們想要的的Jackson屬性呢?最賤的的方式,那就是自己注入一個(gè)ObjectMapper實(shí)例,這樣spring內(nèi)所有通過依賴注入使用ObjectMapper的地方,都會(huì)優(yōu)先使用我們自己注入的那個(gè),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)的時(shí)間序列化格式
        return mapper;
    }
}

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

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

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

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

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

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

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

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

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

相關(guān)文章

  • SpringMVC HttpMessageConverter 匹配規(guī)則

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

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

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

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

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

    springDevBird 評(píng)論0 收藏0
  • PHP反列化漏洞系列之–PHP列化和反列化原理

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

    caozhijian 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<