The following, if there is a problem, please point out, thank you!
Serialization/deserialization of objects everyone should be familiar with it: serialization is the conversion of an object into a binary that can be transferred, and deserialization is the conversion of binary into an object inside the program. Serialization/deserialization is mainly reflected in the process of program I/O, including network I/O and disk I/O.
So what is HTTP serialization and deserialization?
When using SPRINGMVC, we often write this:
@RestController @RequestMapping ( Span class= "st" > "/users" ) public class Usercontroller { @Autowired private userservice userservice; @GetMapping ( "/{id}" ) public User getuserbyid ( @PathVariable long ID) { span class= "kw" >return userservice. getuserbyid (ID); } @PostMapping public User createuser ( Span class= "Fu" > @RequestBody User user) {system.. println ( "Create an User:" + user); return user; }}
There are @responsebody in the @RestController that can help us serialize the user to Resp.body. @RequestBody can help us convert the content of Req.body into a user object. If you are developing a Web application, the two annotations typically correspond to the JSON serialization and deserialization operations. The process of HTTP serialization/deserialization is actually shown here, except that it is somewhat different from normal object serialization, with a higher level of HTTP serialization/deserialization and a conversion between object2object.
There have been netty use of experience on this should be more understanding, Netty in the decoder and encoder there are two basic levels, a low level of a Byte <---> message, binary and program internal message object conversion between the common serialization/ The other is a message <---> message, a conversion between objects within the program, and a higher level of serialization/deserialization.
The processing of the HTTP protocol, TCP byte stream <---> httprequest/httpresponse <---> Internal objects, involves both of these serialization. The first step in SPRINGMVC has been handled by the servlet container (tomcat and so on), and the second step is mainly handled by the framework. The above-mentioned HTTP serialization/deserialization refers to this second step, which is one of the core functions of the Controller layer framework, with this function, it can greatly reduce the amount of code, so that the logic of the controller is more concise and clear, like the code shown above, the method only a single line of code.
SPIRNGMVC The second step, that is, the core of HTTP serialization and deserialization is Httpmessageconverter. There may be some impressions of using older versions of SPRINGMVC, when you need to inject mappingjackson2httpmessageconverter this type of bean into the XML configuration file, telling Springmvc that we need to convert in JSON format, It is an implementation of httpmessageconverter.
In web development, we often use JSON-related frameworks for the second step, because the main development language in Web applications is JS, which is very good for JSON support. But JSON also has a big drawback, most JSON frameworks are not good enough for circular references, and JSON packets are usually larger, and are more expensive than some binary serialization. Many mobile apps also use HTTP to communicate, because this is a JSON-formatted message that has no particular advantage in mobile apps. In this case, we might need some better-performing, smaller-sized serialization frameworks, such as protobuf and so on.
The current version of Springmvc 4.3 has been integrated with the PROTOBUF converter, Org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter, use this class to perform conversions between the message class and the HTTP message in the PROTOBUF. The use of the method is very simple, first rely on the PROTOBUF related jar, the code directly @bean the line, as follows, Springboot will automatically inject and add this converter.
@Bean publicprotobufHttpMessageConverter() { returnnewProtobufHttpMessageConverter(); }
Protobuf related content is not shown here.
Another important point to note, SPRINGMVC can be configured at the same time multiple converter, according to certain rules (mainly Content-type, Accept, controller method Consumes/produces, Converter.mediatype and the order of the Converter four attributes) to choose which one to use, which allows the SPRINGMVC to support multiple message formats on an interface. The details of this rule are explained in detail in the next article.
The following point is how to customize a httpmessageconverter, using Java native serialization as an example, called Javaserializationconverter, Basically imitate Protobufhttpmessageconverter to write.
First inherit abstracthttpmessageconverter, generic class Here are a few ways to choose:
- The widest range of objects can be selected, but object is not all serializable, but can be further controlled in the overridden supports method, so it is possible to select object
- The best fit is serializable, which perfectly satisfies the generic definition and is itself a necessary and sufficient condition for the serialization/deserialization of Java
- Custom base class beans, some technical specifications require that all beans in their code inherit from the same custom base class Basebean, which can be further controlled on the basis of serializable to meet their business requirements
This selects serializable as the generic base class.
The second is to select a mediatype, so that SPRINGMVC can be based on the accept and Content-type is only determined to use JAVASERIALIZATIONCONVERTER, so this mediatype can not be a generic text/ Plain, Application/json, */* This, have a special point, here is used application/x-java-serialization;charset=utf-8. Because Java serialization is binary data, CharSet is not required, but a charset is required in the constructor of mediatype, where UTF-8 is used.
Finally, binary on the computer can not be directly copied content, in order to facilitate testing, using Base64 again, so it is displayed as normal text, easy to test.
The entire code is as follows:
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 classJavaserializationconverterextendsabstracthttpmessageconverter<serializable> {PrivateLogger Logger = loggerfactory.GetLogger(Javaserializationconverter.class); Public Javaserializationconverter() {The type of consumes (req) and produces (RESP) are indicated in the constructor method, indicating that the type will use this converter Super(New mediatype("Application","X-java-serialization", Charset.forname("UTF-8"))); }@Override protected Boolean supports(Class<?> clazz) {//Using Serializable, this can be directly returned true //Use object, here also add serializable interface implementation class judgment //According to your business needs plus other judgments return true; }@Override protectedSerializablereadinternal(CLASS<?extendsSerializable> Clazz, Httpinputmessage inputmessage)throwsIOException, Httpmessagenotreadableexception {byte[] bytes = Streamutils.Copytobytearray(InputMessage.GetBody());//Base64 enables visualization of binary data for easy testingBytearrayinputstream Bytesinput =NewBytearrayinputstream (Base64.Getdecoder().Decode(bytes)); ObjectInputStream ObjectInput =NewObjectInputStream (Bytesinput);Try{return(Serializable) ObjectInput.ReadObject(); }Catch(ClassNotFoundException e) {LOGGER.Error("Exception when Java deserialize, the input is:{}",NewString (Bytes,"UTF-8"), E);return NULL; } }@Override protected void writeinternal(Serializable T, Httpoutputmessage outputmessage)throwsIOException, httpmessagenotwritableexception {bytearrayoutputstream bytesoutput =NewBytearrayoutputstream (); ObjectOutputStream ObjectOutput =NewObjectOutputStream (Bytesoutput); ObjectOutput.writeobject(t);//Base64 enables visualization of binary data for easy testingOutputmessage.GetBody().Write(Base64.Getencoder().encode(Bytesoutput.Tobytearray())); }}
There are three ways to add a converter, and the code and description are as follows:
//Add converter The first way, the code is very simple, is also the recommended way //Doing this springboot will put our custom converter in the order of the highest priority (list of the head) //That is, if there are multiple converter to meet the Accpet/contenttype/mediatype rules, we prefer to use this @Bean PublicJavaserializationconverterJavaserializationconverter() {return New Javaserializationconverter(); }//Add converter the second way //Usually when there is only one custom Webmvcconfigureradapter, the converter (s) added in this method are placed in the highest priority (the head of the list) in turn //Although the first method of code executes first, but the bean is added later than this way, so the priority of mode two is greater than the way one @Override Public void configuremessageconverters(ListThe //Add method can specify the order, and when there are multiple custom webmvcconfigureradapter, you can change the order of each other //But all in front of SPRINGMVC built -in converterConverters.Add(New Javaserializationconverter()); }//Third Way to add converter //The Configuremessageconverters method in the same webmvcconfigureradapter is performed before the Extendmessageconverters method //Can be understood as the last execution in three ways, but you can use the add to specify the order to adjust the priority, or can be used Remove/clear to remove converter, powerful //using Converters.add (XXX) will be placed at the lowest priority (tail of list) //Using Converters.add (0,XXX) will be placed at the highest priority (List's head) @Override Public void extendmessageconverters(ListAdd(New Javaserializationconverter()); }
Use the following data to demonstrate:
// java序列化rO0ABXNyAB1wci5zdHVkeS5zcHJpbmdib290LmJlYW4uVXNlcrt1879rvWjlAgAESgACaWRMAApjcmVhdGVUaW1ldAAQTGphdmEvdXRpbC9EYXRlO0wABWVtYWlsdAASTGphdmEvbGFuZy9TdHJpbmc7TAAEbmFtZXEAfgACeHIAIXByLnN0dWR5LnNwcmluZ2Jvb3QuYmVhbi5CYXNlQmVhbklx6Fsr8RKpAgAAeHAAAAAAAAAAe3NyAA5qYXZhLnV0aWwuRGF0ZWhqgQFLWXQZAwAAeHB3CAAAAWCs8ufxeHQAEGhlbGxvd29ybGRAZy5jb210AApoZWxsb3dvcmxk// json{"id":123,"name":"helloworld","email":"[email protected]","createTime":"2017-12-31 22:21:28"}// 对应的user.toString()User[id=123, name=helloworld, [email protected], createTime=Sun Dec 31 22:21:28 CST 2017]
The demo results are as follows, including an interface with a variety of message format support demos:
1, the request is GET + accept:application/x-java-serialization, return is content-type:application/x-java-serialization;charset= UTF-8 Java serialized Format messages
2, the request is GET + Accept:application/json, the return is content-type:application/json;charset=utf-8 JSON format message
3, the request is POST + accept:application/x-java-serialization + content-type:application/x-java-serialization, return is Content-type:application/x-java-serialization;charset=utf-8 Java serialized Format messages
4, the request is POST + Accept:application/json + content-type:application/x-java-serialization, return is content-type:application/ Json;charset=utf-8 JSON-formatted message
5, the request is POST + Accept:application/json + Content-type:application/json, return is content-type:application/json;charset= UTF-8 JSON-formatted message
6, the request is POST + accept:application/x-java-serialization + Content-type:application/json, return is content-type:application/ X-java-serialization;charset=utf-8 Java serialized Format messages
Here are some other things about HTTP serialization/deserialization.
1. Jackson Configuration
When using Jackson, we typically configure the next objectmapper, the two most common being the time serialization format, and whether to serialize null values. When using Springboot, because Jackson is built-in, how do we configure the Jackson attribute we want? The worst way to do that is to inject yourself into a objectmapper instance, so that all the places in spring that use Objectmapper through dependency injection will take precedence over the one we inject ourselves with, Jacksonconverter is no exception.
/** * Jackson's core is Obje Ctmapper, configure objectmapper here to control certain features of Jackson that Springboot uses */ @ Configuration public class myobjectmpper { public objectmapper getobjectmapper () {Objectmappe R mapper = new objectmapper (); Mapper. setserializationinclusion (Include. non_null ); //does not serialize the Null property of the mapper. setdateformat (new SimpleDateFormat ( "Yyyy-mm-dd HH:mm:ss" )); //the default time-serialized format return mapper; }}
2. Control the serialization of some properties in JSON
The official document said a custom JSON serializers and deserializers, I did not think how to use this, and later on the internet found a better example, that is, the RGB color serialization. The Web page needs the RGB color of the CSS format, and the service may provide three separate byte numbers, which will require changing the JSON serialization/deserialization of the color attribute. Specifically, you can look here.
3. Fastjson Configuration
Maybe you need to use Fastjson at some point, so how do I configure it? Basically the same as SPRINGMVC XML configuration, inject a fastjsonhttpmessageconverter on the line. The simplest is the above configuration Javaserializationconverter way one, the way two and the way three are also OK.
But there will be strange problems (using @jsonfield (Serialize=false, Deserialize=false) annotated createtime to differentiate Fastjson and Jackson):
If you configure Fastjson to be the highest priority and configure the Javaserializationconverter at the same time, you will find that Javaserializationconverter is not used, the request is GET + Accept: Application/x-java-serialization, return is content-type:application/x-java-serialization;charset=utf-8; But the actual content is in JSON format, as follows.
If you configure Fastjson as the lowest priority, whatever else you think will be the result of Jackson serialization. But in fact, you use the browser to knock directly to the Fastjson, with the above get fiddler result is Jackson;
For detailed reasons, the next article explains converter matching rules.
Here is the reason for the important and worthy of the slot, that is fastjsonhttpmessageconverter default registered mediatype */*, and then there is the above request is GET + accept:application/ X-java-serialization, the return is content-type:application/x-java-serialization;charset=utf-8, but the actual content is in JSON format, This trickery behavior is a violation of the HTTP protocol specification.
This code design is really bad, JSON framework should be just JSON, so overbearing, what format to tube, for what!?
Related code:
Https://gitee.com/page12/study-springboot/tree/springboot-3
Https://github.com/page12/study-springboot/tree/springboot-3
Springboot Learning (iii) ———— HTTP serialization and deserialization using Httpmessageconverter