最近在项目当中遇到一个问题,就是使用Dubbo进行调用服务时,实体类中使用 jackson 的JsonNode进行数据传送时,导致序列化失败的问题。这里记录一下Dubbo是如何进行自定义反序列化的。

1. 自定义序列化器

借鉴于Dubbo自带JavaSerializer器,自己修改了源码

public class CustomizeDataDeserializer extends AbstractMapDeserializer {private static final Logger LOG = Logger.getLogger(CustomizeDataDeserializer.class.getName());//序列化对象的类型private final Class<?> type;//实体类中字段所需要的类型private final Map<String, FieldDeserializer> fieldMap;//获取实体类中readResolve方法private final Method readResolve;//获取构造函数private Constructor constructor;//获取构造函数的参数private Object[] constructorArgs;static final Map<String, Boolean> PRIMITIVE_TYPE = new HashMap<String, Boolean>(9) {private static final long serialVersionUID = -3972519966755913466L;{put(Boolean.class.getName(), true);put(Character.class.getName(), true);put(Byte.class.getName(), true);put(Short.class.getName(), true);put(Integer.class.getName(), true);put(Long.class.getName(), true);put(Float.class.getName(), true);put(Double.class.getName(), true);put(Void.class.getName(), true);}};public CustomizeDataDeserializer(Class<?> cl) {type = cl;//解析字段中所需要的反序列化器this.fieldMap = new HashMap<>();getFieldMap(cl, this.fieldMap);//解析readReolve方法readResolve = getReadResolve(cl);if (readResolve != null) {readResolve.setAccessible(true);}//获取所有的构造函数Constructor[] constructors = cl.getDeclaredConstructors();long bestCost = Long.MAX_VALUE;for (int i = 0; i < constructors.length; i++) {Class[] param = constructors[i].getParameterTypes();long cost = 0;for (int j = 0; j < param.length; j++) {cost = 4 * cost;if (Object.class.equals(param[j])) {cost += 1;} else if (String.class.equals(param[j])) {cost += 2;} else if (int.class.equals(param[j])) {cost += 3;} else if (long.class.equals(param[j])) {cost += 4;} else if (param[j].isPrimitive()) {cost += 5;} else {cost += 6;}}if (cost < 0 || cost > (1 << 48)) {cost = 1 << 48;}cost += (long) param.length << 48;if (cost < bestCost) {constructor = constructors[i];bestCost = cost;}}//设置构造函数的权限以及获取参数if (constructor != null) {constructor.setAccessible(true);Class[] params = constructor.getParameterTypes();constructorArgs = new Object[params.length];for (int i = 0; i < params.length; i++) {constructorArgs[i] = getParamArg(params[i]);}}}protected static Object getParamArg(Class cl) {if (!cl.isPrimitive()) {return null;} else if (boolean.class.equals(cl)) {return Boolean.FALSE;} else if (byte.class.equals(cl)) {return new Byte((byte) 0);} else if (short.class.equals(cl)) {return new Short((short) 0);} else if (char.class.equals(cl)) {return new Character((char) 0);} else if (int.class.equals(cl)) {return Integer.valueOf(0);} else if (long.class.equals(cl)) {return Long.valueOf(0);} else if (float.class.equals(cl)) {return Float.valueOf(0);} else if (double.class.equals(cl)) {return Double.valueOf(0);} else {throw new UnsupportedOperationException();}}static void logDeserializeError(Field field, Object obj, Object value,Throwable e)throws IOException {String fieldName = (field.getDeclaringClass().getName()+ "." + field.getName());if (e instanceof HessianFieldException) {throw (HessianFieldException) e;} else if (e instanceof IOException) {throw new HessianFieldException(fieldName + ": " + e.getMessage(), e);}if (value != null) {throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " (" + value + ")"+ " cannot be assigned to '" + field.getType().getName() + "'", e);} else {throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e);}}@Overridepublic Class<?> getType() {return type;}@Overridepublic Object readMap(AbstractHessianInput in)throws IOException {try {//实例化对象出来Object obj = instantiate();return readMap(in, obj);} catch (IOException | RuntimeException e) {throw e;} catch (Exception e) {throw new IOExceptionWrapper(type.getName() + ":" + e.getMessage(), e);}}@Overridepublic Object readObject(AbstractHessianInput in, String[] fieldNames)throws IOException {try {//实例化对象Object obj = instantiate();return readObject(in, obj, fieldNames);} catch (IOException | RuntimeException e) {throw e;} catch (Exception e) {throw new IOExceptionWrapper(type.getName() + ":" + e.getMessage(), e);}}protected Method getReadResolve(Class<?> cl) {for (; cl != null; cl = cl.getSuperclass()) {Method[] methods = cl.getDeclaredMethods();for (int i = 0; i < methods.length; i++) {Method method = methods[i];if (method.getName().equals("readResolve") && method.getParameterTypes().length == 0) {return method;}}}return null;}public Object readMap(AbstractHessianInput in, Object obj)throws IOException {try {int ref = in.addRef(obj);while (!in.isEnd()) {Object key = in.readObject();FieldDeserializer deser = fieldMap.get(key);if (deser != null) {deser.deserialize(in, obj);} else {in.readObject();}}in.readMapEnd();Object resolve = resolve(obj);if (obj != resolve) {in.setRef(ref, resolve);}return resolve;} catch (IOException e) {throw e;} catch (Exception e) {throw new IOExceptionWrapper(e);}}public Object readObject(AbstractHessianInput in,Object obj,String[] fieldNames)throws IOException {try {//将实例化的对象添加到HessianInput引用中去int ref = in.addRef(obj);//编译反序列化器,根据对应的数据类型取出for (int i = 0; i < fieldNames.length; i++) {String name = fieldNames[i];FieldDeserializer deser = fieldMap.get(name);//如果缓存的map中没有找到反序列化器,直接到序列化工厂中查询序列化器if (deser != null) {deser.deserialize(in, obj);} else {//序列化工厂中查询反序列化器(还是当前类,并且调用readObject()方法)in.readObject();}}Object resolve = resolve(obj);if (obj != resolve) {in.setRef(ref, resolve);}return resolve;} catch (IOException e) {throw e;} catch (Exception e) {throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e);}}private Object resolve(Object obj)throws Exception {try {if (readResolve != null) {return readResolve.invoke(obj, new Object[0]);}} catch (InvocationTargetException e) {if (e.getTargetException() != null) {throw e;}}return obj;}protected Object instantiate()throws Exception {try {if (constructor != null) {//通过构造函数创建实例对象return constructor.newInstance(constructorArgs);} else {return type.newInstance();}} catch (Exception e) {throw new HessianProtocolException("'" + type.getName() + "' could not be instantiated", e);}}//读取实体类中字段的类型,并且创建对应的解析器protected void getFieldMap(Class<?> cl, Map<String, FieldDeserializer> fieldMap) {for (; cl != null; cl = cl.getSuperclass()) {Field[] fields = cl.getDeclaredFields();for (int i = 0; i < fields.length; i++) {Field field = fields[i];if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {continue;} else if (fieldMap.get(field.getName()) != null) {continue;}try {field.setAccessible(true);} catch (Throwable e) {LOG.warning("字段权限设置失败");}Class<?> type = field.getType();FieldDeserializer deser;if (String.class.equals(type)) {deser = new StringFieldDeserializer(field);} else if (byte.class.equals(type)) {deser = new ByteFieldDeserializer(field);} else if (short.class.equals(type)) {deser = new ShortFieldDeserializer(field);} else if (int.class.equals(type)) {deser = new IntFieldDeserializer(field);} else if (long.class.equals(type)) {deser = new LongFieldDeserializer(field);} else if (float.class.equals(type)) {deser = new FloatFieldDeserializer(field);} else if (double.class.equals(type)) {deser = new DoubleFieldDeserializer(field);} else if (boolean.class.equals(type)) {deser = new BooleanFieldDeserializer(field);} else if (java.sql.Date.class.equals(type)) {deser = new SqlDateFieldDeserializer(field);} else if (java.sql.Timestamp.class.equals(type)) {deser = new SqlTimestampFieldDeserializer(field);} else if (java.sql.Time.class.equals(type)) {deser = new SqlTimeFieldDeserializer(field);} else if (Map.class.equals(type)&& field.getGenericType() != field.getType()) {deser = new ObjectMapFieldDeserializer(field);} else if (List.class.equals(type)&& field.getGenericType() != field.getType()) {deser = new ObjectListFieldDeserializer(field);} else if (Set.class.equals(type)&& field.getGenericType() != field.getType()) {deser = new ObjectSetFieldDeserializer(field);} else if (JsonNode.class.equals(type)) {//如果是JsonNode,使用对应的反序列化方式deser = new JsonNodeDeserializer(field);} else {deser = new ObjectFieldDeserializer(field);}fieldMap.putIfAbsent(field.getName(), deser);}}}//抽象反序列化器abstract static class FieldDeserializer {abstract void deserialize(AbstractHessianInput in, Object obj)throws IOException;}//对象反序列化器static class ObjectFieldDeserializer extends FieldDeserializer {private final Field field;ObjectFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {Object value = null;try {value = in.readObject(field.getType());field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class BooleanFieldDeserializer extends FieldDeserializer {private final Field field;BooleanFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {boolean value = false;try {value = in.readBoolean();field.setBoolean(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class ByteFieldDeserializer extends FieldDeserializer {private final Field field;ByteFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {int value = 0;try {value = in.readInt();field.setByte(obj, (byte) value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class ShortFieldDeserializer extends FieldDeserializer {private final Field field;ShortFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {int value = 0;try {value = in.readInt();field.setShort(obj, (short) value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class ObjectMapFieldDeserializer extends FieldDeserializer {private final Field field;ObjectMapFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {Object value = null;try {Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();value = in.readObject(field.getType(),isPrimitive(types[0]) ? (Class<?>) types[0] : null,isPrimitive(types[1]) ? (Class<?>) types[1] : null);field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class ObjectListFieldDeserializer extends FieldDeserializer {private final Field field;ObjectListFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {Object value = null;try {Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();value = in.readObject(field.getType(),isPrimitive(types[0]) ? (Class<?>) types[0] : null);field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class ObjectSetFieldDeserializer extends FieldDeserializer {private final Field field;ObjectSetFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {Object value = null;try {Type[] types = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();value = in.readObject(field.getType(),isPrimitive(types[0]) ? (Class<?>) types[0] : null);field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class IntFieldDeserializer extends FieldDeserializer {private final Field field;IntFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {int value = 0;try {value = in.readInt();field.setInt(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class LongFieldDeserializer extends FieldDeserializer {private final Field field;LongFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {long value = 0;try {value = in.readLong();field.setLong(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class FloatFieldDeserializer extends FieldDeserializer {private final Field field;FloatFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {double value = 0;try {value = in.readDouble();field.setFloat(obj, (float) value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class DoubleFieldDeserializer extends FieldDeserializer {private final Field field;DoubleFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {double value = 0;try {value = in.readDouble();field.setDouble(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class StringFieldDeserializer extends FieldDeserializer {private final Field field;StringFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {String value = null;try {value = in.readString();field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class SqlDateFieldDeserializer extends FieldDeserializer {private final Field field;SqlDateFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {java.sql.Date value = null;try {java.util.Date date = (java.util.Date) in.readObject();if (date != null) {value = new java.sql.Date(date.getTime());}field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class SqlTimestampFieldDeserializer extends FieldDeserializer {private final Field field;SqlTimestampFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {java.sql.Timestamp value = null;try {java.util.Date date = (java.util.Date) in.readObject();if (date != null) {value = new java.sql.Timestamp(date.getTime());}field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}static class SqlTimeFieldDeserializer extends FieldDeserializer {private final Field field;SqlTimeFieldDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj)throws IOException {java.sql.Time value = null;try {java.util.Date date = (java.util.Date) in.readObject();if (date != null) {value = new java.sql.Time(date.getTime());}field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}//JsonNode 数据反序列化方式static class JsonNodeDeserializer extends FieldDeserializer {private final Field field;JsonNodeDeserializer(Field field) {this.field = field;}@Overridevoid deserialize(AbstractHessianInput in, Object obj) throws IOException {JsonNode value = null;try {//直接将数据读取出来后强制转换成JsonNode,因为Dubbo序列化时会把类型一起写如到流中,读取出来数据会转换成指定的类型value = (JsonNode) in.readObject();field.set(obj, value);} catch (Exception e) {logDeserializeError(field, obj, value, e);}}}private static boolean isPrimitive(Type type) {try {if (type != null) {if (type instanceof Class<?>) {Class<?> clazz = (Class<?>) type;return clazz.isPrimitive() || PRIMITIVE_TYPE.containsKey(clazz.getName());}}} catch (Exception e) {// ignore exception}return false;}}

2. 创建序列化工厂

public class DubboSerializerFactory extends SerializerFactory {/** SERIALIZER_FACTORY */public static final SerializerFactory SERIALIZER_FACTORY = new DubboSerializerFactory();/*** Fkh dubbo serializer factory** @since 1.3.0*/public DubboSerializerFactory() {}/*** Gets default deserializer *** @param cl cl* @return the default deserializer* @since 1.3.0*/@Overridepublic Deserializer getDefaultDeserializer(Class cl) {return new CustomizeDataDeserializer(cl);}
}

3. 创建序列化对象输入流

public class DubboSeiralizerObjectInput implements ObjectInput {/** H 2 i */private final Hessian2Input h2i;/*** Fkh dubbo seiralizer object input** @param is is* @since 1.3.0*/public DubboSeiralizerObjectInput(InputStream is) {h2i = new Hessian2Input(is);//设置反序列化工厂h2i.setSerializerFactory(DubboSerializerFactory.SERIALIZER_FACTORY);}/*** Read bool** @return the boolean* @throws IOException io exception* @since 1.3.0*/@Overridepublic boolean readBool() throws IOException {return this.h2i.readBoolean();}/*** Read byte** @return the byte* @throws IOException io exception* @since 1.3.0*/@Overridepublic byte readByte() throws IOException {return (byte) h2i.readInt();}/*** Read short** @return the short* @throws IOException io exception* @since 1.3.0*/@Overridepublic short readShort() throws IOException {return (short) h2i.readInt();}/*** Read int** @return the int* @throws IOException io exception* @since 1.3.0*/@Overridepublic int readInt() throws IOException {return h2i.readInt();}/*** Read long** @return the long* @throws IOException io exception* @since 1.3.0*/@Overridepublic long readLong() throws IOException {return h2i.readLong();}/*** Read float** @return the float* @throws IOException io exception* @since 1.3.0*/@Overridepublic float readFloat() throws IOException {return (float) h2i.readDouble();}/*** Read double** @return the double* @throws IOException io exception* @since 1.3.0*/@Overridepublic double readDouble() throws IOException {return h2i.readDouble();}/*** Read bytes** @return the byte [ ]* @throws IOException io exception* @since 1.3.0*/@Overridepublic byte[] readBytes() throws IOException {return h2i.readBytes();}/*** Read utf** @return the string* @throws IOException io exception* @since 1.3.0*/@Override@SuppressWarnings({"checkstyle:LowerCamelCaseVariableNamingRule","PMD.LowerCamelCaseVariableNamingRule"})public String readUTF() throws IOException {return h2i.readString();}/*** Read object** @return the object* @throws IOException io exception* @since 1.3.0*/@Overridepublic Object readObject() throws IOException {return h2i.readObject();}/*** Read object** @param <T> parameter* @param cls cls* @return the t* @throws IOException io exception* @since 1.3.0*/@Override@SuppressWarnings("unchecked")public <T> T readObject(Class<T> cls) throws IOException {return (T) h2i.readObject(cls);}/*** Read object** @param <T>  parameter* @param cls  cls* @param type type* @return the t* @throws IOException io exception* @since 1.3.0*/@Overridepublic <T> T readObject(Class<T> cls, Type type) throws IOException {return readObject(cls);}
}

4. 自定义协议序列化器

这里只实现了反序列化方式,并没有自定义序列化方式

public class CustomizeDataSerialization implements Serialization {/*** 每个序列化器都有一个自己的编号** @return the content type id* @since 1.3.0*/@Overridepublic byte getContentTypeId() {return 26;}/*** Gets content type *** @return the content type* @since 1.3.0*/@Overridepublic String getContentType() {return "x-application/customize";}/*** 使用hessian的序列化方式,暂时不需要自定义** @param url    url* @param output output* @return the object output* @throws IOException io exception* @since 1.3.0*/@Overridepublic ObjectOutput serialize(URL url, OutputStream output) throws IOException {return new Hessian2ObjectOutput(output);}/*** Deserialize** @param url   url* @param input input* @return the object input* @throws IOException io exception* @since 1.3.0*/@Overridepublic ObjectInput deserialize(URL url, InputStream input) throws IOException {return new DubboSeiralizerObjectInput(input);}
}

5. 创建Spi文件

在resources文件下创建路径:

内容:序列化名称+类路径
customize=com.xxx.client.dubbo.serialization.CustomizeDataSerialization

配置文件中引用就可以了

server:port: 18104
dubbo:protocol:port: 28033provider:serialization: customize #自定义序列化方式

自定义Dubbo反序列化相关推荐

  1. 反序列化漏洞攻击原理(Dubbo反序列化漏洞剖析)

    关联文章:给服务端发送自定义类实例序列化数据实现反序列化攻击 一.前言 最近大家都在讨论Dubbo反序列化漏洞问题.想必各个大V也都推送了相关文章.看了下各大文章差不多都是一个套路,两个步骤:第一步开 ...

  2. map insert异常失败_处理dubbo反序列化失败的坑

    前言   今天下午,当我经过一个小时的奋"键"疾"码",准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代 ...

  3. Javaweb安全——Dubbo 反序列化(一)

    Dubbo 反序列化(一) Dubbo 基础 Apache Dubbo 是一款 RPC 服务开发框架.提供三个核心功能:面向接口的远程方法调用.智能容错和负载均衡,以及服务自动注册和发现. 节点角色 ...

  4. java反序列化失败怎么处理_处理dubbo反序列化失败的坑

    kubernetes权威指南从docker到实践 79.8元 包邮 (需用券) 去购买 > 前言 今天下午,当我经过一个小时的奋"键"疾"码",准备好好的 ...

  5. java反序列化流建立失败_关于java:处理dubbo反序列化失败的坑

    前言 今天下午,当我通过一个小时的奋"键"疾"码",筹备好好的审查一下(摸鱼)本人写的代码,通过一段时间审查(摸的差不多了,该上班了),得出一个论断我写的代码很 ...

  6. Beanutils造成dubbo反序列化失败?

    前言   今天下午,当我经过一个小时的奋"键"疾"码",准备好好的审查一下(摸鱼)自己写的代码,经过一段时间审查(摸的差不多了,该下班了),得出一个结论我写的代 ...

  7. C#自定义序列化反序列化与 ISerializable 接口

    ISerializable 接口 MSDN注解:允许对象控制其自己的序列化和反序列化过程. ISerializable 接口的定义: public interface ISerializable {v ...

  8. postman如何改成中文版_在 Windows 上如何用 Postman 重现 Dubbo 反序列化漏洞

    Dubbo 2.7.5 以下的版本暴出了一个反序列化漏洞,那么如何在 windows 上重现这个漏洞呢? 1. 下载官方 demo 代码(暴出的漏洞是 http 协议的,故使用 http 的 demo ...

  9. 自定义fastjson反序列化

    fastjson自定义反序列化实现类,demo对一个字符串进行日期格式化(可兼容多重格式).String转Double和String转Integer. 1.自定义fastjson字符串日期格式化 自定 ...

最新文章

  1. R语言ggplot2可视化散点图并添加平滑曲线、WVPlots包BinaryYScatterPlot函数可视化一起中一个变量为二元离散变量的散点图(随着年龄变化是否有健康保险的客户的比例)
  2. 三、6Gbps SAS冲锋队——Cheetah 15K.7、NS.2
  3. 你真的懂你的用户吗?
  4. dev layoutview 怎么显示大小_「转」磁盘满了,为啥du却显示还有很大空间?
  5. spriteatlas 白屏的问题_Discuz白屏问题解决思路
  6. Matlab之数据的输入与输出
  7. Hotpatching a C Function on x86
  8. VS2017编译Detours1.5
  9. 动态规则最佳入门(转)
  10. hotfox(gyb v2.6)增强
  11. Ubuntu 入门学习之从安装部署到java环境的安装
  12. NLP与对比学习的巧妙融合,简单暴力效果显著!
  13. 大数据、云计算、元宇宙——吉吉拍的探索之路
  14. 新浪微博热搜榜“背后的男人”讲述热搜背后的秘密
  15. 写字机器人软件_被误解的写字机器人应该如何为自己正名?
  16. CSS绘制常见的几何图形
  17. 自制力的本质是什么?怎样才能变得自律?
  18. 生活中的ps!女朋友把菜花烤了一下,结果......
  19. SLIC——代码、改进
  20. 定制linux版本,定制属于自己的Linux操作系统

热门文章

  1. C语言中char*和char[]用法区别分析
  2. 2021-07-04 寄存器读写
  3. 好妈妈胜过好老师-允许孩子慢点长大
  4. 计算机学校迎新条幅,2017学校迎新年横幅标语
  5. hive:统计hive中所有表的大小和创建时间
  6. SAR成像(四):多普勒频移的计算
  7. 解决MediaPlayer: Couldn't open /storage/emulated:java.io.FileNotFoundException: No content provider问题
  8. 牛客网 NC20859 兔子的名字
  9. lua 读取服务器文件是否存在,Lua判断一个目录或文件是否存在的方法
  10. 解决关于加速器加速热点连不上,或者没有网络失效问题