线上代码对日志的记录,重要性自不必说。但是怎样记录日志也是有讲究的!
  日志可以直接在每个方法中进行日志记录,优点是想怎么记就怎么记,缺点是记日志的代码可能会超过你的业务代码,可读性急剧下降,这也是日志框架蓬勃发展的源头。
  日志也可以通过非业务代码侵入的形式进行记录,具体来说就是合作切面(aop)进行日志记录,好处自然是让业务更纯洁,缺点是运行性能会受到影响。
  当然了,前面这些都不是本文的主题。本文的主题是,当你用切面进行日志记录时,然后fastjson是如何把你的业务代码给干掉的。
  JsonObject.toJsonString(pj.getArgs()); 这是一个对入参的一个简单打印,至于使用fastjson的原因,自然是因为其输出字符的易读性。args是一个数组。那么这里会有什么问题?普通的参数自然是没有问题了。问题在于,fastjson转换对象为字符串时,报错了。为什么会这样?

  注: 本文bug所现问题入参类型示例为:

  1. public Object doSth(@RequestBody InBean in, HttpServletRequest request) {
  2. // do sth...
  3. }

  切面报错为:

  1. public Object deal(ProceedingJoinPoint pjp) throws Throwable {
  2. String argsStr = JSONObject.toJSONString(pjp.getArgs());
  3. logger.info("入参:{}", argsStr);
  4. }

解析下fastjson源码过程:

初步进入源码:

  1. // 直接调用json化方法
  2. public static String toJSONString(Object object) {
  3. return toJSONString(object, emptyFilters);
  4. }
  5.  
  6. // 转到复杂参数的调用
  7. public static String toJSONString(Object object, SerializeFilter[] filters, SerializerFeature... features) {
  8. return toJSONString(object, SerializeConfig.globalInstance, filters, null, DEFAULT_GENERATE_FEATURE, features);
  9. }
  10.  
  11. // 最终转换方法,1. 获取writer; 2. 处理filter; 3. 将数据定稿writer缓冲; 4. 返回缓冲数据给调用方
  12. public static String toJSONString(Object object, //
  13. SerializeConfig config, //
  14. SerializeFilter[] filters, //
  15. String dateFormat, //
  16. int defaultFeatures, //
  17. SerializerFeature... features) {
  18. SerializeWriter out = new SerializeWriter(null, defaultFeatures, features);
  19.  
  20. try {
  21. JSONSerializer serializer = new JSONSerializer(out, config);
  22.  
  23. if (dateFormat != null && dateFormat.length() != 0) {
  24. serializer.setDateFormat(dateFormat);
  25. serializer.config(SerializerFeature.WriteDateUseDateFormat, true);
  26. }
  27.  
  28. if (filters != null) {
  29. for (SerializeFilter filter : filters) {
  30. serializer.addFilter(filter);
  31. }
  32. }
  33.  
  34. serializer.write(object); // 最核心方法
  35.  
  36. return out.toString();
  37. } finally {
  38. out.close();
  39. }
  40. }

深入内部调用,追根究底:

  1. // 写方法的进一步操作
  2. public final void write(Object object) {
  3. if (object == null) {
  4. out.writeNull();
  5. return;
  6. }
  7.  
  8. Class<?> clazz = object.getClass();
  9. ObjectSerializer writer = getObjectWriter(clazz); // 关键,返回各种对应的序列化器, 如 ObjectArrayCodec
  10.  
  11. try {
  12. writer.write(this, object, null, null, 0); // 调用对应的序列化器进行处理
  13. } catch (IOException e) {
  14. throw new JSONException(e.getMessage(), e);
  15. }
  16. }
  17.  
  18. // 追踪到在运行write方法时出的问题 ObjectArrayCodec
  19. public final void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)
  20. throws IOException {
  21. SerializeWriter out = serializer.out;
  22.  
  23. Object[] array = (Object[]) object;
  24.  
  25. if (object == null) {
  26. out.writeNull(SerializerFeature.WriteNullListAsEmpty);
  27. return;
  28. }
  29.  
  30. int size = array.length;
  31.  
  32. int end = size - 1;
  33.  
  34. if (end == -1) {
  35. out.append("[]");
  36. return;
  37. }
  38.  
  39. SerialContext context = serializer.context;
  40. serializer.setContext(context, object, fieldName, 0);
  41.  
  42. try {
  43. Class<?> preClazz = null;
  44. ObjectSerializer preWriter = null;
  45. out.append('[');
  46.  
  47. if (out.isEnabled(SerializerFeature.PrettyFormat)) {
  48. serializer.incrementIndent();
  49. serializer.println();
  50. for (int i = 0; i < size; ++i) {
  51. if (i != 0) {
  52. out.write(',');
  53. serializer.println();
  54. }
  55. serializer.write(array[i]);
  56. }
  57. serializer.decrementIdent();
  58. serializer.println();
  59. out.write(']');
  60. return;
  61. }
  62.  
  63. for (int i = 0; i < end; ++i) {
  64. Object item = array[i];
  65.  
  66. if (item == null) {
  67. out.append("null,");
  68. } else {
  69. if (serializer.containsReference(item)) {
  70. serializer.writeReference(item);
  71. } else {
  72. Class<?> clazz = item.getClass();
  73.  
  74. if (clazz == preClazz) {
  75. preWriter.write(serializer, item, null, null, 0);
  76. } else {
  77. preClazz = clazz;
  78. preWriter = serializer.getObjectWriter(clazz);
  79.  
  80. preWriter.write(serializer, item, null, null, 0);
  81. }
  82. }
  83. out.append(',');
  84. }
  85. }
  86.  
  87. // 最后一个数组需要单独处理
  88. Object item = array[end];
  89.  
  90. if (item == null) {
  91. out.append("null]");
  92. } else {
  93. if (serializer.containsReference(item)) {
  94. serializer.writeReference(item);
  95. } else {
  96. serializer.writeWithFieldName(item, end);
  97. }
  98. out.append(']');
  99. }
  100. } finally {
  101. serializer.context = context;
  102. }
  103. }

找到最后个出现问题的地方,发现再往下无法再跟踪:

  1. // 外部简单调用
  2. public final void writeWithFieldName(Object object, Object fieldName) {
  3. writeWithFieldName(object, fieldName, null, 0);
  4. }
  5.  
  6. // 最终报错是在这里
  7. public final void writeWithFieldName(Object object, Object fieldName, Type fieldType, int fieldFeatures) {
  8. try {
  9. if (object == null) {
  10. out.writeNull();
  11. return;
  12. }
  13.  
  14. Class<?> clazz = object.getClass();
  15.  
  16. ObjectSerializer writer = getObjectWriter(clazz); // ASMSerializer_2_RequestFacade
  17.  
  18. writer.write(this, object, fieldName, fieldType, fieldFeatures); // 此处使用asm语言写的代码,无法debug, 但是会抛出 IllegalStateException
  19. } catch (IOException e) {
  20. throw new JSONException(e.getMessage(), e);
  21. }
  22. }

检查最后一次出现的问题,搜索出现的标记,找到动态生成asm代码的文件:

  1. // 动态生成代码 ASMSerializer_2_RequestFacade
  2. public JavaBeanSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) throws Exception {
  3. Class<?> clazz = beanInfo.beanType;
  4. if (clazz.isPrimitive()) {
  5. throw new JSONException("unsupportd class " + clazz.getName());
  6. }
  7.  
  8. JSONType jsonType = clazz.getAnnotation(JSONType.class);
  9.  
  10. FieldInfo[] unsortedGetters = beanInfo.fields;;
  11.  
  12. for (FieldInfo fieldInfo : unsortedGetters) {
  13. if (fieldInfo.field == null //
  14. && fieldInfo.method != null //
  15. && fieldInfo.method.getDeclaringClass().isInterface()) {
  16. return new JavaBeanSerializer(clazz);
  17. }
  18. }
  19.  
  20. FieldInfo[] getters = beanInfo.sortedFields;
  21.  
  22. boolean nativeSorted = beanInfo.sortedFields == beanInfo.fields;
  23.  
  24. if (getters.length > 256) {
  25. return new JavaBeanSerializer(clazz);
  26. }
  27.  
  28. for (FieldInfo getter : getters) {
  29. if (!ASMUtils.checkName(getter.getMember().getName())) {
  30. return new JavaBeanSerializer(clazz);
  31. }
  32. }
  33.  
  34. String className = "ASMSerializer_" + seed.incrementAndGet() + "_" + clazz.getSimpleName();
  35. String packageName = ASMSerializerFactory.class.getPackage().getName();
  36. String classNameType = packageName.replace('.', '/') + "/" + className;
  37. String classNameFull = packageName + "." + className;
  38.  
  39. ClassWriter cw = new ClassWriter();
  40. cw.visit(V1_5 //
  41. , ACC_PUBLIC + ACC_SUPER //
  42. , classNameType //
  43. , JavaBeanSerializer //
  44. , new String[] { ObjectSerializer } //
  45. );
  46.  
  47. for (FieldInfo fieldInfo : getters) {
  48. if (fieldInfo.fieldClass.isPrimitive() //
  49. //|| fieldInfo.fieldClass.isEnum() //
  50. || fieldInfo.fieldClass == String.class) {
  51. continue;
  52. }
  53.  
  54. new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_fieldType", "Ljava/lang/reflect/Type;") //
  55. .visitEnd();
  56.  
  57. if (List.class.isAssignableFrom(fieldInfo.fieldClass)) {
  58. new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_list_item_ser_",
  59. ObjectSerializer_desc) //
  60. .visitEnd();
  61. }
  62.  
  63. new FieldWriter(cw, ACC_PUBLIC, fieldInfo.name + "_asm_ser_", ObjectSerializer_desc) //
  64. .visitEnd();
  65. }
  66.  
  67. MethodVisitor mw = new MethodWriter(cw, ACC_PUBLIC, "<init>", "(" + desc(SerializeBeanInfo.class) + ")V", null, null);
  68. mw.visitVarInsn(ALOAD, 0);
  69. mw.visitVarInsn(ALOAD, 1);
  70. mw.visitMethodInsn(INVOKESPECIAL, JavaBeanSerializer, "<init>", "(" + desc(SerializeBeanInfo.class) + ")V");
  71.  
  72. // init _asm_fieldType
  73. for (int i = 0; i < getters.length; ++i) {
  74. FieldInfo fieldInfo = getters[i];
  75. if (fieldInfo.fieldClass.isPrimitive() //
  76. // || fieldInfo.fieldClass.isEnum() //
  77. || fieldInfo.fieldClass == String.class) {
  78. continue;
  79. }
  80.  
  81. mw.visitVarInsn(ALOAD, 0);
  82.  
  83. if (fieldInfo.method != null) {
  84. mw.visitLdcInsn(com.alibaba.fastjson.asm.Type.getType(desc(fieldInfo.declaringClass)));
  85. mw.visitLdcInsn(fieldInfo.method.getName());
  86. mw.visitMethodInsn(INVOKESTATIC, type(ASMUtils.class), "getMethodType",
  87. "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Type;");
  88.  
  89. } else {
  90. mw.visitVarInsn(ALOAD, 0);
  91. mw.visitLdcInsn(i);
  92. mw.visitMethodInsn(INVOKESPECIAL, JavaBeanSerializer, "getFieldType", "(I)Ljava/lang/reflect/Type;");
  93. }
  94.  
  95. mw.visitFieldInsn(PUTFIELD, classNameType, fieldInfo.name + "_asm_fieldType", "Ljava/lang/reflect/Type;");
  96. }
  97.  
  98. mw.visitInsn(RETURN);
  99. mw.visitMaxs(4, 4);
  100. mw.visitEnd();
  101.  
  102. boolean DisableCircularReferenceDetect = false;
  103. if (jsonType != null) {
  104. for (SerializerFeature featrues : jsonType.serialzeFeatures()) {
  105. if (featrues == SerializerFeature.DisableCircularReferenceDetect) {
  106. DisableCircularReferenceDetect = true;
  107. break;
  108. }
  109. }
  110. }
  111.  
  112. // 0 write
  113. // 1 writeNormal
  114. // 2 writeNonContext
  115. for (int i = 0; i < 3; ++i) {
  116. String methodName;
  117. boolean nonContext = DisableCircularReferenceDetect;
  118. boolean writeDirect = false;
  119. if (i == 0) {
  120. methodName = "write";
  121. writeDirect = true;
  122. } else if (i == 1) {
  123. methodName = "writeNormal";
  124. } else {
  125. writeDirect = true;
  126. nonContext = true;
  127. methodName = "writeDirectNonContext";
  128. }
  129.  
  130. Context context = new Context(getters, beanInfo, classNameType, writeDirect,
  131. nonContext);
  132.  
  133. mw = new MethodWriter(cw, //
  134. ACC_PUBLIC, //
  135. methodName, //
  136. "(L" + JSONSerializer
  137. + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V", //
  138. null, //
  139. new String[] { "java/io/IOException" } //
  140. );
  141.  
  142. mw.visitVarInsn(ALOAD, Context.serializer);
  143. mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
  144. mw.visitVarInsn(ASTORE, context.var("out"));
  145.  
  146. if ((!nativeSorted) //
  147. && !context.writeDirect) {
  148.  
  149. if (jsonType == null || jsonType.alphabetic()) {
  150. Label _else = new Label();
  151.  
  152. mw.visitVarInsn(ALOAD, context.var("out"));
  153. mw.visitMethodInsn(INVOKEVIRTUAL, SerializeWriter, "isSortField", "()Z");
  154.  
  155. mw.visitJumpInsn(IFNE, _else);
  156. mw.visitVarInsn(ALOAD, 0);
  157. mw.visitVarInsn(ALOAD, 1);
  158. mw.visitVarInsn(ALOAD, 2);
  159. mw.visitVarInsn(ALOAD, 3);
  160. mw.visitVarInsn(ALOAD, 4);
  161. mw.visitVarInsn(ILOAD, 5);
  162. mw.visitMethodInsn(INVOKEVIRTUAL, classNameType,
  163. "writeUnsorted", "(L" + JSONSerializer
  164. + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
  165. mw.visitInsn(RETURN);
  166.  
  167. mw.visitLabel(_else);
  168. }
  169. }
  170.  
  171. // isWriteDoubleQuoteDirect
  172. if (context.writeDirect && !nonContext) {
  173. Label _direct = new Label();
  174. Label _directElse = new Label();
  175.  
  176. mw.visitVarInsn(ALOAD, 0);
  177. mw.visitVarInsn(ALOAD, Context.serializer);
  178. mw.visitMethodInsn(INVOKEVIRTUAL, JavaBeanSerializer, "writeDirect", "(L" + JSONSerializer + ";)Z");
  179. mw.visitJumpInsn(IFNE, _directElse);
  180.  
  181. mw.visitVarInsn(ALOAD, 0);
  182. mw.visitVarInsn(ALOAD, 1);
  183. mw.visitVarInsn(ALOAD, 2);
  184. mw.visitVarInsn(ALOAD, 3);
  185. mw.visitVarInsn(ALOAD, 4);
  186. mw.visitVarInsn(ILOAD, 5);
  187. mw.visitMethodInsn(INVOKEVIRTUAL, classNameType,
  188. "writeNormal", "(L" + JSONSerializer
  189. + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
  190. mw.visitInsn(RETURN);
  191.  
  192. mw.visitLabel(_directElse);
  193. mw.visitVarInsn(ALOAD, context.var("out"));
  194. mw.visitLdcInsn(SerializerFeature.DisableCircularReferenceDetect.mask);
  195. mw.visitMethodInsn(INVOKEVIRTUAL, SerializeWriter, "isEnabled", "(I)Z");
  196. mw.visitJumpInsn(IFEQ, _direct);
  197.  
  198. mw.visitVarInsn(ALOAD, 0);
  199. mw.visitVarInsn(ALOAD, 1);
  200. mw.visitVarInsn(ALOAD, 2);
  201. mw.visitVarInsn(ALOAD, 3);
  202. mw.visitVarInsn(ALOAD, 4);
  203. mw.visitVarInsn(ILOAD, 5);
  204. mw.visitMethodInsn(INVOKEVIRTUAL, classNameType, "writeDirectNonContext",
  205. "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V");
  206. mw.visitInsn(RETURN);
  207.  
  208. mw.visitLabel(_direct);
  209. }
  210.  
  211. mw.visitVarInsn(ALOAD, Context.obj); // obj
  212. mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
  213. mw.visitVarInsn(ASTORE, context.var("entity")); // obj
  214. generateWriteMethod(clazz, mw, getters, context);
  215. mw.visitInsn(RETURN);
  216. mw.visitMaxs(7, context.variantIndex + 2);
  217. mw.visitEnd();
  218. }
  219.  
  220. if (!nativeSorted) {
  221. // sortField support
  222. Context context = new Context(getters, beanInfo, classNameType, false,
  223. DisableCircularReferenceDetect);
  224.  
  225. mw = new MethodWriter(cw, ACC_PUBLIC, "writeUnsorted",
  226. "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V",
  227. null, new String[] { "java/io/IOException" });
  228.  
  229. mw.visitVarInsn(ALOAD, Context.serializer);
  230. mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
  231. mw.visitVarInsn(ASTORE, context.var("out"));
  232.  
  233. mw.visitVarInsn(ALOAD, Context.obj); // obj
  234. mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
  235. mw.visitVarInsn(ASTORE, context.var("entity")); // obj
  236.  
  237. generateWriteMethod(clazz, mw, unsortedGetters, context);
  238.  
  239. mw.visitInsn(RETURN);
  240. mw.visitMaxs(7, context.variantIndex + 2);
  241. mw.visitEnd();
  242. }
  243.  
  244. // 0 writeAsArray
  245. // 1 writeAsArrayNormal
  246. // 2 writeAsArrayNonContext
  247. for (int i = 0; i < 3; ++i) {
  248. String methodName;
  249. boolean nonContext = DisableCircularReferenceDetect;
  250. boolean writeDirect = false;
  251. if (i == 0) {
  252. methodName = "writeAsArray";
  253. writeDirect = true;
  254. } else if (i == 1) {
  255. methodName = "writeAsArrayNormal";
  256. } else {
  257. writeDirect = true;
  258. nonContext = true;
  259. methodName = "writeAsArrayNonContext";
  260. }
  261.  
  262. Context context = new Context(getters, beanInfo, classNameType, writeDirect,
  263. nonContext);
  264.  
  265. mw = new MethodWriter(cw, ACC_PUBLIC, methodName,
  266. "(L" + JSONSerializer + ";Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/reflect/Type;I)V",
  267. null, new String[] { "java/io/IOException" });
  268.  
  269. mw.visitVarInsn(ALOAD, Context.serializer);
  270. mw.visitFieldInsn(GETFIELD, JSONSerializer, "out", SerializeWriter_desc);
  271. mw.visitVarInsn(ASTORE, context.var("out"));
  272.  
  273. mw.visitVarInsn(ALOAD, Context.obj); // obj
  274. mw.visitTypeInsn(CHECKCAST, type(clazz)); // serializer
  275. mw.visitVarInsn(ASTORE, context.var("entity")); // obj
  276. generateWriteAsArray(clazz, mw, getters, context);
  277. mw.visitInsn(RETURN);
  278. mw.visitMaxs(7, context.variantIndex + 2);
  279. mw.visitEnd();
  280. }
  281.  
  282. byte[] code = cw.toByteArray();
  283.  
  284. Class<?> exampleClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length);
  285. Constructor<?> constructor = exampleClass.getConstructor(SerializeBeanInfo.class);
  286. Object instance = constructor.newInstance(beanInfo);
  287.  
  288. return (JavaBeanSerializer) instance;
  289. }

再回退到上一步,查找调用链路:

  1. // 如果没有找到,就创建一个类
  2. public ObjectSerializer getObjectWriter(Class<?> clazz) {
  3. return getObjectWriter(clazz, true);
  4. }
  5.  
  6. // classLoader 多次加载
  7. private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
  8. ObjectSerializer writer = serializers.get(clazz);
  9.  
  10. if (writer == null) {
  11. try {
  12. final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  13. for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
  14. if (!(o instanceof AutowiredObjectSerializer)) {
  15. continue;
  16. }
  17.  
  18. AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
  19. for (Type forType : autowired.getAutowiredFor()) {
  20. put(forType, autowired);
  21. }
  22. }
  23. } catch (ClassCastException ex) {
  24. // skip
  25. }
  26.  
  27. writer = serializers.get(clazz);
  28. }
  29.  
  30. if (writer == null) {
  31. final ClassLoader classLoader = JSON.class.getClassLoader();
  32. if (classLoader != Thread.currentThread().getContextClassLoader()) {
  33. try {
  34. for (Object o : ServiceLoader.load(AutowiredObjectSerializer.class, classLoader)) {
  35.  
  36. if (!(o instanceof AutowiredObjectSerializer)) {
  37. continue;
  38. }
  39.  
  40. AutowiredObjectSerializer autowired = (AutowiredObjectSerializer) o;
  41. for (Type forType : autowired.getAutowiredFor()) {
  42. put(forType, autowired);
  43. }
  44. }
  45. } catch (ClassCastException ex) {
  46. // skip
  47. }
  48.  
  49. writer = serializers.get(clazz);
  50. }
  51. }
  52.  
  53. if (writer == null) {
  54. if (Map.class.isAssignableFrom(clazz)) {
  55. put(clazz, MapSerializer.instance);
  56. } else if (List.class.isAssignableFrom(clazz)) {
  57. put(clazz, ListSerializer.instance);
  58. } else if (Collection.class.isAssignableFrom(clazz)) {
  59. put(clazz, CollectionCodec.instance);
  60. } else if (Date.class.isAssignableFrom(clazz)) {
  61. put(clazz, DateCodec.instance);
  62. } else if (JSONAware.class.isAssignableFrom(clazz)) {
  63. put(clazz, JSONAwareSerializer.instance);
  64. } else if (JSONSerializable.class.isAssignableFrom(clazz)) {
  65. put(clazz, JSONSerializableSerializer.instance);
  66. } else if (JSONStreamAware.class.isAssignableFrom(clazz)) {
  67. put(clazz, MiscCodec.instance);
  68. } else if (clazz.isEnum() || (clazz.getSuperclass() != null && clazz.getSuperclass().isEnum())) {
  69. JSONType jsonType = clazz.getAnnotation(JSONType.class);
  70. if (jsonType != null && jsonType.serializeEnumAsJavaBean()) {
  71. put(clazz, createJavaBeanSerializer(clazz));
  72. } else {
  73. put(clazz, EnumSerializer.instance);
  74. }
  75. } else if (clazz.isArray()) {
  76. Class<?> componentType = clazz.getComponentType();
  77. ObjectSerializer compObjectSerializer = getObjectWriter(componentType);
  78. put(clazz, new ArraySerializer(componentType, compObjectSerializer));
  79. } else if (Throwable.class.isAssignableFrom(clazz)) {
  80. SerializeBeanInfo beanInfo = TypeUtils.buildBeanInfo(clazz, null, propertyNamingStrategy);
  81. beanInfo.features |= SerializerFeature.WriteClassName.mask;
  82. put(clazz, new JavaBeanSerializer(beanInfo));
  83. } else if (TimeZone.class.isAssignableFrom(clazz) || Map.Entry.class.isAssignableFrom(clazz)) {
  84. put(clazz, MiscCodec.instance);
  85. } else if (Appendable.class.isAssignableFrom(clazz)) {
  86. put(clazz, AppendableSerializer.instance);
  87. } else if (Charset.class.isAssignableFrom(clazz)) {
  88. put(clazz, ToStringSerializer.instance);
  89. } else if (Enumeration.class.isAssignableFrom(clazz)) {
  90. put(clazz, EnumerationSerializer.instance);
  91. } else if (Calendar.class.isAssignableFrom(clazz) //
  92. || XMLGregorianCalendar.class.isAssignableFrom(clazz)) {
  93. put(clazz, CalendarCodec.instance);
  94. } else if (Clob.class.isAssignableFrom(clazz)) {
  95. put(clazz, ClobSeriliazer.instance);
  96. } else if (TypeUtils.isPath(clazz)) {
  97. put(clazz, ToStringSerializer.instance);
  98. } else if (Iterator.class.isAssignableFrom(clazz)) {
  99. put(clazz, MiscCodec.instance);
  100. } else {
  101. String className = clazz.getName();
  102. if (className.startsWith("java.awt.") //
  103. && AwtCodec.support(clazz) //
  104. ) {
  105. // awt
  106. if (!awtError) {
  107. try {
  108. put(Class.forName("java.awt.Color"), AwtCodec.instance);
  109. put(Class.forName("java.awt.Font"), AwtCodec.instance);
  110. put(Class.forName("java.awt.Point"), AwtCodec.instance);
  111. put(Class.forName("java.awt.Rectangle"), AwtCodec.instance);
  112. } catch (Throwable e) {
  113. awtError = true;
  114. // skip
  115. }
  116. }
  117. return AwtCodec.instance;
  118. }
  119.  
  120. // jdk8
  121. if ((!jdk8Error) //
  122. && (className.startsWith("java.time.") //
  123. || className.startsWith("java.util.Optional") //
  124. )) {
  125. try {
  126. put(Class.forName("java.time.LocalDateTime"), Jdk8DateCodec.instance);
  127. put(Class.forName("java.time.LocalDate"), Jdk8DateCodec.instance);
  128. put(Class.forName("java.time.LocalTime"), Jdk8DateCodec.instance);
  129. put(Class.forName("java.time.ZonedDateTime"), Jdk8DateCodec.instance);
  130. put(Class.forName("java.time.OffsetDateTime"), Jdk8DateCodec.instance);
  131. put(Class.forName("java.time.OffsetTime"), Jdk8DateCodec.instance);
  132. put(Class.forName("java.time.ZoneOffset"), Jdk8DateCodec.instance);
  133. put(Class.forName("java.time.ZoneRegion"), Jdk8DateCodec.instance);
  134. put(Class.forName("java.time.Period"), Jdk8DateCodec.instance);
  135. put(Class.forName("java.time.Duration"), Jdk8DateCodec.instance);
  136. put(Class.forName("java.time.Instant"), Jdk8DateCodec.instance);
  137.  
  138. put(Class.forName("java.util.Optional"), OptionalCodec.instance);
  139. put(Class.forName("java.util.OptionalDouble"), OptionalCodec.instance);
  140. put(Class.forName("java.util.OptionalInt"), OptionalCodec.instance);
  141. put(Class.forName("java.util.OptionalLong"), OptionalCodec.instance);
  142.  
  143. writer = serializers.get(clazz);
  144. if (writer != null) {
  145. return writer;
  146. }
  147. } catch (Throwable e) {
  148. // skip
  149. jdk8Error = true;
  150. }
  151. }
  152.  
  153. if ((!oracleJdbcError) //
  154. && className.startsWith("oracle.sql.")) {
  155. try {
  156. put(Class.forName("oracle.sql.DATE"), DateCodec.instance);
  157. put(Class.forName("oracle.sql.TIMESTAMP"), DateCodec.instance);
  158.  
  159. writer = serializers.get(clazz);
  160. if (writer != null) {
  161. return writer;
  162. }
  163. } catch (Throwable e) {
  164. // skip
  165. oracleJdbcError = true;
  166. }
  167. }
  168.  
  169. if ((!springfoxError) //
  170. && className.equals("springfox.documentation.spring.web.json.Json")) {
  171. try {
  172. put(Class.forName("springfox.documentation.spring.web.json.Json"), //
  173. SwaggerJsonSerializer.instance);
  174.  
  175. writer = serializers.get(clazz);
  176. if (writer != null) {
  177. return writer;
  178. }
  179. } catch (ClassNotFoundException e) {
  180. // skip
  181. springfoxError = true;
  182. }
  183. }
  184.  
  185. if ((!guavaError) //
  186. && className.startsWith("com.google.common.collect.")) {
  187. try {
  188. put(Class.forName("com.google.common.collect.HashMultimap"), //
  189. GuavaCodec.instance);
  190. put(Class.forName("com.google.common.collect.LinkedListMultimap"), //
  191. GuavaCodec.instance);
  192. put(Class.forName("com.google.common.collect.ArrayListMultimap"), //
  193. GuavaCodec.instance);
  194. put(Class.forName("com.google.common.collect.TreeMultimap"), //
  195. GuavaCodec.instance);
  196.  
  197. writer = serializers.get(clazz);
  198. if (writer != null) {
  199. return writer;
  200. }
  201. } catch (ClassNotFoundException e) {
  202. // skip
  203. guavaError = true;
  204. }
  205. }
  206.  
  207. if (className.equals("net.sf.json.JSONNull")) {
  208. try {
  209. put(Class.forName("net.sf.json.JSONNull"), //
  210. MiscCodec.instance);
  211. } catch (ClassNotFoundException e) {
  212. // skip
  213. }
  214. writer = serializers.get(clazz);
  215. if (writer != null) {
  216. return writer;
  217. }
  218. }
  219.  
  220. if (TypeUtils.isProxy(clazz)) {
  221. Class<?> superClazz = clazz.getSuperclass();
  222.  
  223. ObjectSerializer superWriter = getObjectWriter(superClazz);
  224. put(clazz, superWriter);
  225. return superWriter;
  226. }
  227.  
  228. // asm创建类是在这里生效的
  229. if (create) {
  230. put(clazz, createJavaBeanSerializer(clazz));
  231. }
  232. }
  233.  
  234. writer = serializers.get(clazz);
  235. }
  236. return writer;
  237. }

往前追踪,发现入口:

  1. // TypeUtils, 获取所有get方法。
  2. public static List<FieldInfo> computeGetters(Class<?> clazz, //
  3. JSONType jsonType, //
  4. Map<String, String> aliasMap, //
  5. Map<String, Field> fieldCacheMap, //
  6. boolean sorted, //
  7. PropertyNamingStrategy propertyNamingStrategy //
  8. ) {
  9. Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();
  10.  
  11. for (Method method : clazz.getMethods()) {
  12. String methodName = method.getName();
  13. int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
  14. String label = null;
  15.  
  16. if (Modifier.isStatic(method.getModifiers())) {
  17. continue;
  18. }
  19.  
  20. if (method.getReturnType().equals(Void.TYPE)) {
  21. continue;
  22. }
  23.  
  24. if (method.getParameterTypes().length != 0) {
  25. continue;
  26. }
  27.  
  28. if (method.getReturnType() == ClassLoader.class) {
  29. continue;
  30. }
  31.  
  32. if (method.getName().equals("getMetaClass")
  33. && method.getReturnType().getName().equals("groovy.lang.MetaClass")) {
  34. continue;
  35. }
  36.  
  37. JSONField annotation = method.getAnnotation(JSONField.class);
  38.  
  39. if (annotation == null) {
  40. annotation = getSuperMethodAnnotation(clazz, method);
  41. }
  42.  
  43. if (annotation != null) {
  44. if (!annotation.serialize()) {
  45. continue;
  46. }
  47.  
  48. ordinal = annotation.ordinal();
  49. serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());
  50. parserFeatures = Feature.of(annotation.parseFeatures());
  51.  
  52. if (annotation.name().length() != 0) {
  53. String propertyName = annotation.name();
  54.  
  55. if (aliasMap != null) {
  56. propertyName = aliasMap.get(propertyName);
  57. if (propertyName == null) {
  58. continue;
  59. }
  60. }
  61.  
  62. FieldInfo fieldInfo = new FieldInfo(propertyName, method, null, clazz, null, ordinal,
  63. serialzeFeatures, parserFeatures, annotation, null, label);
  64. fieldInfoMap.put(propertyName, fieldInfo);
  65. continue;
  66. }
  67.  
  68. if (annotation.label().length() != 0) {
  69. label = annotation.label();
  70. }
  71. }
  72.  
  73. if (methodName.startsWith("get")) {
  74. if (methodName.length() < 4) {
  75. continue;
  76. }
  77.  
  78. if (methodName.equals("getClass")) {
  79. continue;
  80. }
  81.  
  82. if (methodName.equals("getDeclaringClass") && clazz.isEnum()) {
  83. continue;
  84. }
  85.  
  86. char c3 = methodName.charAt(3);
  87.  
  88. String propertyName;
  89. if (Character.isUpperCase(c3) //
  90. || c3 > 512 // for unicode method name
  91. ) {
  92. if (compatibleWithJavaBean) {
  93. propertyName = decapitalize(methodName.substring(3));
  94. } else {
  95. propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
  96. }
  97. propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName,3);
  98. } else if (c3 == '_') {
  99. propertyName = methodName.substring(4);
  100. } else if (c3 == 'f') {
  101. propertyName = methodName.substring(3);
  102. } else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
  103. propertyName = decapitalize(methodName.substring(3));
  104. } else {
  105. continue;
  106. }
  107.  
  108. boolean ignore = isJSONTypeIgnore(clazz, propertyName);
  109.  
  110. if (ignore) {
  111. continue;
  112. }
  113. //假如bean的field很多的情况一下,轮询时将大大降低效率
  114. Field field = ParserConfig.getFieldFromCache(propertyName, fieldCacheMap);
  115.  
  116. if (field == null && propertyName.length() > 1) {
  117. char ch = propertyName.charAt(1);
  118. if (ch >= 'A' && ch <= 'Z') {
  119. String javaBeanCompatiblePropertyName = decapitalize(methodName.substring(3));
  120. field = ParserConfig.getFieldFromCache(javaBeanCompatiblePropertyName, fieldCacheMap);
  121. }
  122. }
  123.  
  124. JSONField fieldAnnotation = null;
  125. if (field != null) {
  126. fieldAnnotation = field.getAnnotation(JSONField.class);
  127.  
  128. if (fieldAnnotation != null) {
  129. if (!fieldAnnotation.serialize()) {
  130. continue;
  131. }
  132.  
  133. ordinal = fieldAnnotation.ordinal();
  134. serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
  135. parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
  136.  
  137. if (fieldAnnotation.name().length() != 0) {
  138. propertyName = fieldAnnotation.name();
  139.  
  140. if (aliasMap != null) {
  141. propertyName = aliasMap.get(propertyName);
  142. if (propertyName == null) {
  143. continue;
  144. }
  145. }
  146. }
  147.  
  148. if (fieldAnnotation.label().length() != 0) {
  149. label = fieldAnnotation.label();
  150. }
  151. }
  152. }
  153.  
  154. if (aliasMap != null) {
  155. propertyName = aliasMap.get(propertyName);
  156. if (propertyName == null) {
  157. continue;
  158. }
  159. }
  160.  
  161. if (propertyNamingStrategy != null) {
  162. propertyName = propertyNamingStrategy.translate(propertyName);
  163. }
  164.  
  165. FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
  166. annotation, fieldAnnotation, label);
  167. fieldInfoMap.put(propertyName, fieldInfo);
  168. }
  169.  
  170. if (methodName.startsWith("is")) {
  171. if (methodName.length() < 3) {
  172. continue;
  173. }
  174.  
  175. if (method.getReturnType() != Boolean.TYPE
  176. && method.getReturnType() != Boolean.class) {
  177. continue;
  178. }
  179.  
  180. char c2 = methodName.charAt(2);
  181.  
  182. String propertyName;
  183. if (Character.isUpperCase(c2)) {
  184. if (compatibleWithJavaBean) {
  185. propertyName = decapitalize(methodName.substring(2));
  186. } else {
  187. propertyName = Character.toLowerCase(methodName.charAt(2)) + methodName.substring(3);
  188. }
  189. propertyName = getPropertyNameByCompatibleFieldName(fieldCacheMap, methodName, propertyName,2);
  190. } else if (c2 == '_') {
  191. propertyName = methodName.substring(3);
  192. } else if (c2 == 'f') {
  193. propertyName = methodName.substring(2);
  194. } else {
  195. continue;
  196. }
  197.  
  198. Field field = ParserConfig.getFieldFromCache(propertyName,fieldCacheMap);
  199.  
  200. if (field == null) {
  201. field = ParserConfig.getFieldFromCache(methodName,fieldCacheMap);
  202. }
  203.  
  204. JSONField fieldAnnotation = null;
  205. if (field != null) {
  206. fieldAnnotation = field.getAnnotation(JSONField.class);
  207.  
  208. if (fieldAnnotation != null) {
  209. if (!fieldAnnotation.serialize()) {
  210. continue;
  211. }
  212.  
  213. ordinal = fieldAnnotation.ordinal();
  214. serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
  215. parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
  216.  
  217. if (fieldAnnotation.name().length() != 0) {
  218. propertyName = fieldAnnotation.name();
  219.  
  220. if (aliasMap != null) {
  221. propertyName = aliasMap.get(propertyName);
  222. if (propertyName == null) {
  223. continue;
  224. }
  225. }
  226. }
  227.  
  228. if (fieldAnnotation.label().length() != 0) {
  229. label = fieldAnnotation.label();
  230. }
  231. }
  232. }
  233.  
  234. if (aliasMap != null) {
  235. propertyName = aliasMap.get(propertyName);
  236. if (propertyName == null) {
  237. continue;
  238. }
  239. }
  240.  
  241. if (propertyNamingStrategy != null) {
  242. propertyName = propertyNamingStrategy.translate(propertyName);
  243. }
  244.  
  245. //优先选择get
  246. if (fieldInfoMap.containsKey(propertyName)) {
  247. continue;
  248. }
  249.  
  250. FieldInfo fieldInfo = new FieldInfo(propertyName, method, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
  251. annotation, fieldAnnotation, label);
  252. fieldInfoMap.put(propertyName, fieldInfo);
  253. }
  254. }
  255.  
  256. for (Field field : clazz.getFields()) {
  257. if (Modifier.isStatic(field.getModifiers())) {
  258. continue;
  259. }
  260.  
  261. JSONField fieldAnnotation = field.getAnnotation(JSONField.class);
  262.  
  263. int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
  264. String propertyName = field.getName();
  265. String label = null;
  266. if (fieldAnnotation != null) {
  267. if (!fieldAnnotation.serialize()) {
  268. continue;
  269. }
  270.  
  271. ordinal = fieldAnnotation.ordinal();
  272. serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
  273. parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
  274.  
  275. if (fieldAnnotation.name().length() != 0) {
  276. propertyName = fieldAnnotation.name();
  277. }
  278.  
  279. if (fieldAnnotation.label().length() != 0) {
  280. label = fieldAnnotation.label();
  281. }
  282. }
  283.  
  284. if (aliasMap != null) {
  285. propertyName = aliasMap.get(propertyName);
  286. if (propertyName == null) {
  287. continue;
  288. }
  289. }
  290.  
  291. if (propertyNamingStrategy != null) {
  292. propertyName = propertyNamingStrategy.translate(propertyName);
  293. }
  294.  
  295. if (!fieldInfoMap.containsKey(propertyName)) {
  296. FieldInfo fieldInfo = new FieldInfo(propertyName, null, field, clazz, null, ordinal, serialzeFeatures, parserFeatures,
  297. null, fieldAnnotation, label);
  298. fieldInfoMap.put(propertyName, fieldInfo);
  299. }
  300. }
  301.  
  302. List<FieldInfo> fieldInfoList = new ArrayList<FieldInfo>();
  303.  
  304. boolean containsAll = false;
  305. String[] orders = null;
  306.  
  307. JSONType annotation = clazz.getAnnotation(JSONType.class);
  308. if (annotation != null) {
  309. orders = annotation.orders();
  310.  
  311. if (orders != null && orders.length == fieldInfoMap.size()) {
  312. containsAll = true;
  313. for (String item : orders) {
  314. if (!fieldInfoMap.containsKey(item)) {
  315. containsAll = false;
  316. break;
  317. }
  318. }
  319. } else {
  320. containsAll = false;
  321. }
  322. }
  323.  
  324. if (containsAll) {
  325. for (String item : orders) {
  326. FieldInfo fieldInfo = fieldInfoMap.get(item);
  327. fieldInfoList.add(fieldInfo);
  328. }
  329. } else {
  330. for (FieldInfo fieldInfo : fieldInfoMap.values()) {
  331. fieldInfoList.add(fieldInfo);
  332. }
  333.  
  334. if (sorted) {
  335. Collections.sort(fieldInfoList);
  336. }
  337. }
  338.  
  339. return fieldInfoList;
  340. }
  341.  
  342. // SerializeConfig, 创建java bean.
  343.  
  344. public ObjectSerializer createJavaBeanSerializer(SerializeBeanInfo beanInfo) {
  345. JSONType jsonType = beanInfo.jsonType;
  346.  
  347. if (jsonType != null) {
  348. Class<?> serializerClass = jsonType.serializer();
  349. if (serializerClass != Void.class) {
  350. try {
  351. Object seralizer = serializerClass.newInstance();
  352. if (seralizer instanceof ObjectSerializer) {
  353. return (ObjectSerializer) seralizer;
  354. }
  355. } catch (Throwable e) {
  356. // skip
  357. }
  358. }
  359.  
  360. if (jsonType.asm() == false) {
  361. asm = false;
  362. }
  363. }
  364.  
  365. Class<?> clazz = beanInfo.beanType;
  366. if (!Modifier.isPublic(beanInfo.beanType.getModifiers())) {
  367. return new JavaBeanSerializer(beanInfo);
  368. }
  369.  
  370. boolean asm = this.asm;
  371.  
  372. if (asm && asmFactory.classLoader.isExternalClass(clazz)
  373. || clazz == Serializable.class || clazz == Object.class) {
  374. asm = false;
  375. }
  376.  
  377. if (asm && !ASMUtils.checkName(clazz.getSimpleName())) {
  378. asm = false;
  379. }
  380.  
  381. if (asm) {
  382. for(FieldInfo fieldInfo : beanInfo.fields){
  383. Field field = fieldInfo.field;
  384. if (field != null && !field.getType().equals(fieldInfo.fieldClass)) {
  385. asm = false;
  386. break;
  387. }
  388.  
  389. Method method = fieldInfo.method;
  390. if (method != null && !method.getReturnType().equals(fieldInfo.fieldClass)) {
  391. asm = false;
  392. break;
  393. }
  394.  
  395. JSONField annotation = fieldInfo.getAnnotation();
  396.  
  397. if (annotation == null) {
  398. continue;
  399. }
  400. if ((!ASMUtils.checkName(annotation.name())) //
  401. || annotation.format().length() != 0
  402. || annotation.jsonDirect()
  403. || annotation.serializeUsing() != Void.class
  404. ) {
  405. asm = false;
  406. break;
  407. }
  408. }
  409. }
  410.  
  411. if (asm) {
  412. try {
  413. ObjectSerializer asmSerializer = createASMSerializer(beanInfo);
  414. if (asmSerializer != null) {
  415. return asmSerializer;
  416. }
  417. } catch (ClassFormatError e) {
  418. // skip
  419. } catch (ClassCastException e) {
  420. // skip
  421. } catch (Throwable e) {
  422. throw new JSONException("create asm serializer error, class "
  423. + clazz, e);
  424. }
  425. }
  426.  
  427. return new JavaBeanSerializer(beanInfo);
  428. }

使用asmClassLoader加载动态类:

  1. // 创建asmSerializer, 最后调用classloader加载类
  2. private final JavaBeanSerializer createASMSerializer(SerializeBeanInfo beanInfo) throws Exception {
  3. JavaBeanSerializer serializer = asmFactory.createJavaBeanSerializer(beanInfo);
  4.  
  5. for (int i = 0; i < serializer.sortedGetters.length; ++i) {
  6. FieldSerializer fieldDeser = serializer.sortedGetters[i];
  7. Class<?> fieldClass = fieldDeser.fieldInfo.fieldClass;
  8. if (fieldClass.isEnum()) {
  9. ObjectSerializer fieldSer = this.getObjectWriter(fieldClass);
  10. if (!(fieldSer instanceof EnumSerializer)) {
  11. serializer.writeDirect = false;
  12. }
  13. }
  14. }
  15.  
  16. return serializer;
  17. }

  最后的代码报错是:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false),由于是写在动态生成的代码文件中,比较难查,解决问题已足够,不再深究。

  理解这里的原因后,再来看问题,就简单多了,排除掉不支持转换的类或者使用其他方式获取想要的参数。
  思考:如果让自己来设计框架,也需要考虑得更多;在用别人的框架的时候,也以一个开发者的角度考虑问题,如果不想考虑,可以直接参考其源码,发现其中的蹊跷;另外,测试一定是必不可少的,你可以借助测试部门,也可以自行测试,但是一定要考虑全面;最后,在做一些公用方法的变动时,一定不要太大意,宁可相信自己不无知,也不要相信代码一定不会出错,有条件情况下,尽可以使用try catch将其包裹,这样,在出问题的时候,也能迅速定位。
  题外话:错误一定是会犯的,但是不要关键时候犯。

来啊踩fastjson打印入参导致业务跑偏的坑的更多相关文章

  1. dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析

    问题描述&模拟 线上登录接口,通过监控查看,有类型转换异常,具体报错如下图 此报错信息是dubbo consumer端显示,且登录大部分是正常,有少量部分会报类型转换异常,同事通过更换方法名+ ...

  2. 【C语言】 strlen()入参空指针导致段错误

    背景: 在工作中调试sqlite3相关代码的时候,调用printf()打印sqlite3_exec()的执行日志:因为sqlite3_exec()保存日志的参数传入时为NULL,且没有执行错误,所以再 ...

  3. MyBatis版本升级导致OffsetDateTime入参解析异常问题复盘

    背景 最近有一个数据统计服务需要升级SpringBoot的版本,由1.5.x.RELEASE直接升级到2.3.0.RELEASE,考虑到没有用到SpringBoot的内建SPI,升级过程算是顺利.但是 ...

  4. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  5. Spring Boot 自定义注解,AOP 切面统一打印出入参请求日志

    其实,小哈在之前就出过一篇关于如何使用 AOP 切面统一打印请求日志的文章,那为什么还要再出一篇呢?没东西写了? 哈哈,当然不是!原因是当时的实现方案还是存在缺陷的,原因如下: 不够灵活,由于是以所有 ...

  6. Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId

    一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...

  7. SpringBoot2 参数管理实践,入参出参与校验

    一.参数管理 在编程系统中,为了能写出良好的代码,会根据是各种设计模式.原则.约束等去规范代码,从而提高代码的可读性.复用性.可修改,实际上个人觉得,如果写出的代码很好,即别人修改也无法破坏原作者的思 ...

  8. 从输出日志中提取接口的入参和返回做为用例导入到excel中

    1  背景 接口用例已经在项目中的yml文件中编写,但是yml文件不能做为交付文档用,本文对工作中从接口输出日志中提取用例信息,并导入到excel文件中做了总些 2  工具 idea,notepad+ ...

  9. Oracle存储过程入参传入List集合的小例子

    第一步:创建一个对象类型 create or replace type STUDENT as object( id ), name ), age ) ); / 第二步:创建一个数组类型 (任意选择下面 ...

随机推荐

  1. srs之深入浅出看流媒体

    本文转载:https://blog.csdn.net/zjqlovell/article/details/50786040 CDN这几年爆炸式增长,带宽提速是根源,而HTTP始终还是那个屌样,因此目前 ...

  2. 自适应Web主页

    HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  3. Linux下docker报错syntax error:unexpected protocol at end of statement

    ---恢复内容开始--- [Linux]Shell脚本“syntax error: unexpected end of file”原因及处理 :::https://blog.csdn.net/u013 ...

  4. EasyPR源码剖析(7):车牌判断之SVM

    前面的文章中我们主要介绍了车牌定位的相关技术,但是定位出来的相关区域可能并非是真实的车牌区域,EasyPR通过SVM支持向量机,一种机器学习算法来判定截取的图块是否是真的“车牌”,本节主要对相关的技术 ...

  5. eclipse创建scrapy项目

    1. 您必须创建一个新的Scrapy项目. 进入您打算存储代码的目录中(比如否F:/demo),运行下列命令: scrapy startproject tutorial 2.在eclipse中创建一个 ...

  6. net::ERR_CONNECTION_RESET 问题排查

    后台服务器代码有问题 实体不对称,导致映射不对

  7. 关于sql优化的一些点

    慢查询日志 参考:https://www.cnblogs.com/saneri/p/6656161.html 查询是否开启慢查询日志: show variables like '%slow_query ...

  8. dubbo-2.5.6优雅停机研究

    不优雅的停机: 当进程存在正在运行的线程时,如果直接执行kill -9 pid时,那么这个正在执行的线程被中断,就好像一个机器运行中突然遭遇断电的情况,所导致的结果是造成服务调用的消费端报错,也有可能 ...

  9. MongoDB学习记录(四) - MongoDB的"增查改删"操作之"改"

    更新文档主要有以下几种方法: db.collection.updateOne(filter, update, options) db.collection.updateMany(filter, upd ...

  10. 《团队-爬取豆瓣top250-开发文档》

    项目托管平台地址:https://github.com/gengwenhao/GetTop250.git 本项目能够爬取豆瓣电影TOP250并向用户展示电影的排名,分数等