Author: Eagle Soup
Link: https://juejin.cn/post/7156439842958606349
Online Incident Review
Some time ago, I added a very simple function. When I coded before going online at night, review
I thought of the company’s hard-working and enterprising values and temporarily added a line of log logs. I thought that there was basically no problem with just one line of simple logs. As a result, there were a bunch of alarms after the launch. Hurry up Rolled back the code, found the problem, deleted the code that added the log, and went online again.
Scenario restoration
defines a
CountryDTO
public class CountryDTO {
private String country;
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return this.country;
}
public Boolean isChinaName() {
return this.country.equals("中国");
}
}
Define the test class
FastJonTest
public class FastJonTest {
@Test
public void testSerialize() {
CountryDTO countryDTO = new CountryDTO();
String str = JSON.toJSONString(countryDTO);
System.out.println(str);
}
}
Error when running 空指针
:
From the error message, it can be seen that the method was executed during the serialization process isChinaName()
. At this time, this.country
the variable is empty, so the problem arises:
- Why is serialization performed
isChinaName()
? - By extension, what methods will be executed during the serialization process?
Recommend an open source and free Spring Boot practical project:
Source code analysis
Observe the stack information of the call link through debug
In the call chain, a class ASMSerializer_1_CountryDTO.write
is dynamically generated FastJson
using technology ,asm
ASMSerializer_1_CountryDTO
One of the usage scenarios of asm technology is to dynamically generate classes to replace
java
reflection, so as to avoid reflection overhead during repeated execution
JavaBeanSerizlier serialization principle
It can be seen from the figure below that in the process of serialization, the method JavaBeanSerializer
of the class is mainly called write()
.
It JavaBeanSerializer
is mainly obtained through getObjectWriter()
methods, through getObjectWriter()
debugging the execution process, to find more critical com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer
methods, and then to findcom.alibaba.fastjson.util.TypeUtils#computeGetters
public static List<FieldInfo> computeGetters(Class<?> clazz, //
JSONType jsonType, //
Map<String,String> aliasMap, //
Map<String,Field> fieldCacheMap, //
boolean sorted, //
PropertyNamingStrategy propertyNamingStrategy //
){
//省略部分代码....
Method[] methods = clazz.getMethods();
for(Method method : methods){
//省略部分代码...
if(method.getReturnType().equals(Void.TYPE)){
continue;
}
if(method.getParameterTypes().length != 0){
continue;
}
//省略部分代码...
JSONField annotation = TypeUtils.getAnnotation(method, JSONField.class);
//省略部分代码...
if(annotation != null){
if(!annotation.serialize()){
continue;
}
if(annotation.name().length() != 0){
//省略部分代码...
}
}
if(methodName.startsWith("get")){
//省略部分代码...
}
if(methodName.startsWith("is")){
//省略部分代码...
}
}
}
From the code, it can be roughly divided into three situations:
@JSONField(.serialize = false, name = "xxx")
annotationgetXxx()
: The method starting with getisXxx()
: method starting with is
Serialization flowchart
sample code
/**
* case1: @JSONField(serialize = false)
* case2: getXxx()返回值为void
* case3: isXxx()返回值不等于布尔类型
* case4: @JSONType(ignores = "xxx")
*/
@JSONType(ignores = "otherName")
public class CountryDTO {
private String country;
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return this.country;
}
public static void queryCountryList() {
System.out.println("queryCountryList()执行!!");
}
public Boolean isChinaName() {
System.out.println("isChinaName()执行!!");
return true;
}
public String getEnglishName() {
System.out.println("getEnglishName()执行!!");
return "lucy";
}
public String getOtherName() {
System.out.println("getOtherName()执行!!");
return "lucy";
}
/**
* case1: @JSONField(serialize = false)
*/
@JSONField(serialize = false)
public String getEnglishName2() {
System.out.println("getEnglishName2()执行!!");
return "lucy";
}
/**
* case2: getXxx()返回值为void
*/
public void getEnglishName3() {
System.out.println("getEnglishName3()执行!!");
}
/**
* case3: isXxx()返回值不等于布尔类型
*/
public String isChinaName2() {
System.out.println("isChinaName2()执行!!");
return "isChinaName2";
}
}
The result of the operation is:
isChinaName()执行!!
getEnglishName()执行!!
{"chinaName":true,"englishName":"lucy"}
code specification
It can be seen that there are still many serialization rules. For example, sometimes you need to pay attention to the return value, sometimes you need to pay attention to the number of parameters, sometimes you need to pay attention to @JSONType
annotations, and sometimes you need to pay attention @JSONField
to annotations; The degree of mastery of knowledge points is different, and this variance can easily lead to code problems, so try to have a recommended solution. It is recommended to use @JSONField(serialize = false)
the explicit annotation method not to participate in serialization. The following is the code after using the recommended solution. Can you see at a glance which methods do not need to participate in serialization.
public class CountryDTO {
private String country;
public void setCountry(String country) {
this.country = country;
}
public String getCountry() {
return this.country;
}
@JSONField(serialize = false)
public static void queryCountryList() {
System.out.println("queryCountryList()执行!!");
}
public Boolean isChinaName() {
System.out.println("isChinaName()执行!!");
return true;
}
public String getEnglishName() {
System.out.println("getEnglishName()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public String getOtherName() {
System.out.println("getOtherName()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public String getEnglishName2() {
System.out.println("getEnglishName2()执行!!");
return "lucy";
}
@JSONField(serialize = false)
public void getEnglishName3() {
System.out.println("getEnglishName3()执行!!");
}
@JSONField(serialize = false)
public String isChinaName2() {
System.out.println("isChinaName2()执行!!");
return "isChinaName2";
}
}
Three cases of high frequency serialization
The above process basically follows the discovery of problems --> principle analysis --> problem solving --> sublimation (programming specification).
- Focusing on business: solving problems -> how to choose a good solution -> how to expand n system applications with a good solution;
- Focus on technology: solve a single problem, and master the principles of this line along a single problem.
Recent hot article recommendation:
1. 1,000+ Java interview questions and answers (2022 latest version)
2. Brilliant! Java coroutines are coming. . .
3. Spring Boot 2.x tutorial, too comprehensive!
5. The latest release of "Java Development Manual (Songshan Edition)", download quickly!
Feel good, don't forget to like + forward!