01的在线事故。网上事故审查前段时间加了一个很简单的功能。晚上上线前review代码的时候,想到了公司勤奋进取的价值观,临时加上了。一行log日志,我觉得就一行简单的日志基本没有问题。结果刚上线就报了一堆告警。赶紧回滚代码,发现问题,把添加日志的代码删掉,重新上线。02.场景还原定义一个CountryDTOpublicclassCountryDTO{privateStringcountry;publicvoidsetCountry(Stringcountry){this.country=country;}publicStringgetCountry(){返回这个国家;}publicBooleanisChinaName(){returnthis.country.equals("China");}}定义测试类FastJonTestpublicclassFastJonTest{@TestpublicvoidtestSerialize(){CountryDTOcountryDTO=newCountryDTO();字符串str=JSON.toJSONString(countryDTO);System.out.println(str);}}运行时报空指针错误:从报错信息可以看出序列化过程中执行了isChinaName()方法。这时候变量this.country是空的,那么问题来了:为什么要执行序列化呢?isChinaName()怎么样?推而广之,序列化过程中会执行哪些方法呢?03.源码分析通过debug观察调用链接的堆栈信息。调用链中的ASMSerializer_1_CountryDTO.write是FastJson使用asm技术动态生成的类ASMSerializer_1_CountryDTO,其中asm技术的使用场景是用动态生成的类代替java反射,避免重复执行时的反射开销。JavaBeanSerializer主要通过getObjectWriter()方法获取。通过调试getObjectWriter()执行过程,找到比较关键的com.alibaba.fastjson.serializer.SerializeConfig#createJavaBeanSerializer方法,然后com.alibaba.fastjson.util.TypeUtils#computeGetterspublicstaticListcomputeGetters(Class>clazz,//JSONTypejsonType,//MapaliasMap,//MapfieldCacheMap,//布尔排序,//PropertyNamingStrategypropertyNamingStrategy//){//省略一些代码。...方法[]方法=clazz.getMethods();for(Methodmethod:methods){//省略一些代码...if(method.getReturnType().equals(Void.TYPE)){continue;}如果(方法。getParameterTypes().length!=0){继续;}//省略部分代码...JSONFieldannotation=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")){//省略部分代码...}}}从代码来看大致有三种情况:@JSONField(.serialize=false,name="xxx")注解getXxx():methodatthebeginningofgetisXxx():methodisXxx()开头是05,序列化流程图06,示例代码/***case1:@JSONField(serialize=false)*case2:getXxx()返回值为void*case3:isXxx()返回值不等于布尔类型*case4:@JSONType(ignores="xxx")*/@JSONType(ignores="otherName")publicclassCountryDTO{privateSt环国;publicvoidsetCountry(Stringcountry){this.country=country;}publicStringgetCountry(){返回这个国家;}publicstaticvoidqueryCountryList(){System.out.println("queryCountryList()执行!!");}publicBooleanisChinaName(){System.out.println("isChinaName()执行!!");返回真;}publicStringgetEnglishName(){System.out.println("getEnglishName()执行!!");返回“露西”;}publicStringgetOtherName(){System.out.println("getOtherName()执行!!");返回“露西”;}/***case1:@JSONField(serialize=false)*/@JSONField(serialize=false)publicStringgetEnglishName2(){System.out.println("getEnglishName2()执行!!");返回“露西”;}/***case2:getXxx()返回值为void*/publicvoidgetEnglishName3(){System.out.println("getEnglishName3()被执行!!");}/***case3:isXxx()返回值不等于布尔类型*/publicStringisChinaName2(){System.out.println("isChinaName2()执行!!");返回“是中国名2”;}}运行结果为:执行了isChinaName()!!getEnglishName()!!{"chinaName":true,"englishName":"lucy"}07、代码规范可以查看规则还是很多的序列化。比如有时候需要关注返回值,有时候需要关注参数个数,有时候需要关注@JSONType注解,有时候需要关注@JSONField注解;当有多种方法来区分一个事物时,因为团队成员的知识点程度不同。这种差异很容易导致代码问题,所以尽量在这里有一个推荐的解决方案。推荐使用@JSONField(serialize=false)显式标记方法,不参与序列化。下面是推荐的解决方案代码之后,能不能一眼看出哪些方法不需要参与序列化。公共类CountryDTO{私有字符串国家;publicvoidsetCountry(Stringcountry){this.country=country;}publicStringgetCountry(){返回这个国家;}@JSONField(serialize=false)publicstaticvoidqueryCountryList(){System.out.println("queryCountryList()执行!!");}publicBooleanisChinaName(){System.out.println("isChinaName()执行!!");返回真;}publicStringgetEnglishName(){System.out.println("getEnglishName()执行!!");返回“露西”;}@JSONField(serialize=false)publicStringgetOtherName(){System.out.println("getOtherName()执行!!");返回“露西”;}@JSONField(serialize=false)publicStringgetEnglishName2(){System.out.println("getEnglishName2()执行!!");返回“露西”;}@JSONField(序列化=假)publicvoidgetEnglishName3(){System.out.println("getEnglishName3()执行!!");}@JSONField(serialize=false)publicStringisChinaName2(){System.out.println("isChinaName2()执行!!");返回“是中国名2”;}}08、高频连载的三个案例上面的流程基本上是发现问题-->原理分析-->解决问题-->围绕业务的升华(编程规范):解决问题-->如何选择好的解决方案->如何用好的解决方案扩展n个系统应用;aroundtechnology:解决单个问题,掌握沿着单个问题这条线的原理。