序列化和反序列化:将数据变得更加通用化

序列化与反序列化简介

序列化和反序列化是计算机领域中常用的概念,用于将对象或数据结构转换为字节序列(序列化)和将字节序列转换回对象或数据结构(反序列化)。

序列化是指将对象或数据结构转换为字节序列的过程。通过序列化,可以将对象保存到文件、数据库或进行网络传输。在序列化过程中,对象的状态和数据会被转换为一系列的字节,以便能够在不同的环境中进行传输或持久存储。

反序列化则是将字节序列转换回对象或数据结构的过程。通过反序列化,可以重新构造出原始的对象或数据结构,使其可以被程序使用。

序列化和反序列化可以实现跨平台的数据交换。例如,在网络通信中,可以将对象序列化后发送给其他计算机,接收端再进行反序列化,从而实现数据的传输和共享。

常见的序列化格式包括 JSON、XML、Protocol Buffers(protobuf)等。不同的序列化格式有各自的特点和适用场景,选择适合的序列化格式可以提高数据传输效率和方便性。

需要注意的是,在进行序列化和反序列化时,应该确保数据的完整性和安全性。对于敏感数据,可以采用加密等方式进行保护,以防止数据泄露或篡改。此外,在不同语言或不同版本间进行序列化和反序列化时,还需要特别注意对象的兼容性和数据格式的一致性。

序列化目的

序列化的主要目的是将对象或数据结构转换为字节序列,以便在不同环境中进行传输或持久存储。具体来说,序列化的目的有以下几个:

  1. 数据交换:序列化可以将数据转换为跨平台兼容的格式,使得数据可以在不同的系统、编程语言之间进行交换和共享。
  2. 持久化存储:将对象序列化后,可以将其保存到磁盘、数据库等介质中,实现数据的持久化存储,防止程序退出或计算机宕机导致数据丢失。
  3. 网络通信:在网络通信中,可以将对象序列化后通过网络发送到其他计算机,接收端再进行反序列化,从而实现数据的传输和共享。
  4. 缓存优化:序列化后的数据可以被缓存,当需要时直接从缓存中读取,避免了频繁的数据库查询,提高了性能。

总的来说,序列化的目的是为了方便数据的传输、保存和共享,同时还可以提高程序的性能和响应速度。

序列化框架选择

在选择序列化和反序列化框架的时候,主要从以下两个方面进行考虑:

(1)结果数据大小; 原则上来说,序列化后的数据越小,传输效率越高;

(2)结构复杂程度;结构复杂度会影响序列化和发序列化的效率,结构越复杂,越耗时。

根据以上两点,对于性能要求不是太高的服务器程序,可以选择Json文本格式的序列化框架;对于性能要求比较高的程序程序,则应该选择传输效率更高的二进制序列化框架,建议使用Protobuf。

Json

Json(JavaScript Object Notation JS对象)是一种轻量级的数据交换格式。它是基于ECMAScript 的一个子集。采用完全独立于编程语言的文本格式来存储和表示数据。

Json 协议是一种文本协议,易于阅读和编写,同时也易于机器解析和生成,并能有效地提升网络传输协效率。

JSON格式具有以下特点:

  • 可读性高:JSON使用简洁明了的文本格式,易于人类阅读和理解。
  • 轻量级:相较于其他数据交换格式如XML,JSON的数据表示更为紧凑,占用更少的存储空间和传输带宽。
  • 平台无关性:JSON格式在不同的编程语言和平台之间具有良好的兼容性,可以方便地进行数据交换和共享。
  • 支持多种数据类型:JSON支持包括字符串、数字、布尔值、数组、对象和null在内的多种数据类型。这使得JSON能够灵活地表示各种数据结构和复杂对象。
  • 易于解析和生成:绝大多数编程语言都提供了JSON的解析和生成库,使得操作JSON数据变得十分方便和高效。
  • 可扩展性:JSON格式支持通过嵌套和组合来表示更复杂的数据结构,可以根据具体需求进行扩展和定制。

在JSON中,数据以键值对的方式表示,键是一个字符串,值可以是字符串、数字、布尔值、数组、对象或null。简单的JSON示例如下:

{"name": "张娜","age": 30,"isStudent": false,"hobbies": ["阅读", "游泳"],"address": {"street": "安德里北街12号","city": "北京市"},"score": null
}

Json序列化和反序列化开源库

java处理Json数据有三个比较流行的开源类库:

(1)阿里的FastJson

阿里巴巴的FastJson 是一个高性能的 JSON 库。 FastJson 库采用独创的快速算法,将 JSON 转成 POJO 的速度提升到极致,从性能上说,其反序列化速度超过其他 JSON 开 源库。

FastJson 在复杂类型的 POJO 转换 JSON (序列化)时,可能会 出现一些引用类 型问题而导致 JSON 转换出错,需要进行引用的定制。

(2)谷歌的Gson

Google的 Gson 开源库是一个功能齐全的 JSON 解析库,起源于 Google 公司内部需求而由 Google 自行研发而来,在 2008 年 5 月公开发布第一版之后已被许多公司或用户应用。 Gson 可 以完成复杂类型的 POJO 和 JSON 字符串的相互转换,转换的能力非常强。

(3)开源社区的Jackson

Jackson 是一个简单的、基于java的Json开源库。使用Jackson开源库,可以轻松地将java Pojo对象转换成Json、XML;式串;同样也可以方便地将 JSON 、 XML 字符串转换成 Java POJO 对象。

Jackson 开源库的优点是:所依赖的 Jar 包较少、简单易用、性能也还不错, 另外 Jackson 社区相对比较活跃。

Jackson 开源库的缺点是:对于复杂 POJO 类型、复杂的集合 Map 、 List 的转换结果,不是标准的 JSON 格式,或者会出现一些问题。

在实际生产中,比较主流的策略是Gson+FastJson相互结合的JsonUtil类,在Pojo序列化为JSON字符串的应用场景(序列化场景)使用Gson库;在JSON 字符串反序列化成POJO的应用场景(反序列化)使用FastJson库。JsonUtil类如下:


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.crazymaker.springcloud.common.result.RestOut;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.util.Map;public class JsonUtil {//谷歌 GsonBuilder 构造器static GsonBuilder gb = new GsonBuilder();private static final Gson gson;static {//不需要html escapegb.disableHtmlEscaping();gson = gb.create();}//序列化:使用谷歌 Gson 将 POJO 转成字符串public static String pojoToJson(Object obj) {String json = gson.toJson(obj);return json;}//反序列化:使用阿里fastJson 将字符串转成 POJO对象public static <T> T jsonToPojo(String json, Class<T> tClass) {T t = JSON.parseObject(json, tClass);return t;}}

在2022年5月的时候,中共电信天翼云发布了FastJson ≤ 1.2.80 具有严重、高危的反序列化远程代码执行漏洞,在这种情况下,就需要通过快速切换json组件来解决子此问题,最佳策略是策略模式+工具类的结合,实现业务应用兼容主要的json组件,根据具体的场景和各种突发事件能够进行快速、灵活的组件切换。

策略模式(Strategy)

策略模式 属于对象的行为模式,主要是针对一组不同的算法,抽象出一组共同的接口或抽象类,然后根据每一个单独的算法封装具体的实现类,从而使得它们可以相互替换。

策略模式的优势是可以在不影响到客户端的情况下,实现具体算法的切换。

策略模式的类图如下:

在这里插入图片描述

使用策略模式(Strategy)实现不同的Json开源组件之间的切换,其具体类图如下:
在这里插入图片描述

抽象策略接口 JsonStrategy,定义了一组抽象的方法,如toJson()-序列化、fromJson()-反序列化。


public interface JsonStrategy {...String toJson(Object object);String toJson(Object object, String dateFormatPattern);<T> T fromJson(String json, Class<T> valueType);...
}

具体策略类FastJsonStrategy、GsonJSONStrategy、JackSonStrategy,分别使用FastJson、Gson、JackSon三个主流的开源组件,完成Pojo对象的序列化和反序列化。 这样设计遵循了软件设计的开闭原则,在后续若想新增一个新的序列化/反序列化组件,只需要新增一个JsonStrategy实现类即可。

FastJsonStrategy 类:

public class FastJsonStrategy implements JsonStrategy {public FastJsonStrategy() {}...//序列化@Overridepublic String toJson(Object object) {return JSON.toJSONString(object);}@Overridepublic String toJson(Object object, String dateFormatPattern) {return JSON.toJSONStringWithDateFormat(object, dateFormatPattern, SerializerFeature.WriteDateUseDateFormat);}//反序列化@Overridepublic <T> T fromJson(String json, Class<T> valueType) {return JSON.parseObject(json, valueType);}...
}

GsonStrategy 类:


public class GsonStrategy implements JsonStrategy {public static Gson gson;public static GsonBuilder gsonBuilder;public GsonStrategy() {gsonBuilder = new GsonBuilder();//不需要html escapegsonBuilder.disableHtmlEscaping();// 解决Gson序列化时出现整型变为浮点型的问题gsonBuilder.registerTypeAdapter(new TypeToken<Map<Object, Object>>() { }.getType(),(JsonDeserializer<Map<Object, Object>>) (jsonElement, type, jsonDeserializationContext) -> {Map<Object, Object> map = new LinkedHashMap<>();JsonObject jsonObject = jsonElement.getAsJsonObject();Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();for (Map.Entry<String, JsonElement> entry : entrySet) {Object obj = entry.getValue();if (obj instanceof JsonPrimitive) {map.put(entry.getKey(), ((JsonPrimitive) obj).getAsString());} else {map.put(entry.getKey(), obj);}}return map;});gsonBuilder.registerTypeAdapter(new TypeToken<List<Object>>() { }.getType(),(JsonDeserializer<List<Object>>) (jsonElement, type, jsonDeserializationContext) -> {List<Object> list = new LinkedList<>();JsonArray jsonArray = jsonElement.getAsJsonArray();for (int i = 0; i < jsonArray.size(); i++) {if (jsonArray.get(i).isJsonObject()) {JsonObject jsonObject = jsonArray.get(i).getAsJsonObject();Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();list.addAll(entrySet);} else if (jsonArray.get(i).isJsonPrimitive()) {list.add(jsonArray.get(i));}}return list;});gson = gsonBuilder.create();}...@Overridepublic String toJson(Object object) {return gson.toJson(object);}@Overridepublic String toJson(Object object, String dateFormatPattern) {gson = gsonBuilder.setDateFormat(dateFormatPattern).create();return gson.toJson(object);}@Overridepublic <T> T fromJson(String json, Class<T> valueType) {return gson.fromJson(json, valueType);}
...
}

JacksonJsonStrategy类:

public class JacksonJsonStrategy implements JsonStrategy {public static ObjectMapper objectMapper;public JacksonJsonStrategy() {// 禁止时间格式序列化为时间戳if (objectMapper == null) {objectMapper = new ObjectMapper().findAndRegisterModules().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);}}...@Overridepublic String toJson(Object object) {try {return objectMapper.writeValueAsString(object);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}@Overridepublic String toJson(Object object, String dateFormatPattern) {SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatPattern);try {return objectMapper.writer(dateFormat).writeValueAsString(object);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}@Overridepublic <T> T fromJson(String json, Class<T> valueType) {try {return objectMapper.readValue(json, valueType);} catch (IOException e) {throw new RuntimeException(e);}}...
}

环境类JsonContext,此类是模块内部和模块外部之间的纽带,对于模块内部来说,JsonContext类根据配置文件文件中的类型配置,初始化具体的JsonStrategy实现类,并且将其引用保存在内部成员变量中;对于模块外部来说,JsonContext 类为他们提供JsonStrategy引用,供外部Client客户程序使用。

在配置文件system.properties 中配置所使用的json组件,具体配置如下:

json.strategy=Jackson

读取配置文件

@ConfigFileAnno(file = "/system.properties")
public class    SystemConfig extends ConfigProperties
{//依照属性,从配置文件中,装载配置项static ConfigProperties singleton= new SystemConfig("/system.properties");private SystemConfig(String fileName){super(fileName);super.loadFromFile();}/*** json的类型: gson/fastjson/Jackson** json.strategy=fastjson*/public static final String JSON_STRATEGY= singleton.getValue("json.strategy");
}

JsonContext类:

@Slf4j
public abstract class JsonContext {private  static volatile JsonStrategy strategy;private JsonContext() {}private static final String CLASS_TYPE_JACKSON = "com.fasterxml.jackson.databind.ObjectMapper";private static final String CLASS_TYPE_FASTJSON = "com.alibaba.fastjson.JSON";private static final String CLASS_TYPE_GSON = "com.google.gson.Gson";/*** json的类型: gson/fastjson/Jackson*/private static final String JACKSON = "Jackson";private static final String FASTJSON = "fastjson";private static final String GSON = "gson";private static JsonStrategy loadFromConfig() {String jsonType = SystemConfig.JSON_STRATEGY;switch (jsonType) {case JACKSON:if (isClassPresent(CLASS_TYPE_JACKSON)) {log.info("used jackson");return new JacksonJsonStrategy();} else {log.error("jackson not found");throw new RuntimeException("未找到jackson的依赖");}case FASTJSON:if (isClassPresent(CLASS_TYPE_FASTJSON)) {log.info("used fastjson");return new FastJsonStrategy();} else {log.error("fastjson not found");throw new RuntimeException("未找到fastjson的依赖");}case GSON:if (isClassPresent(CLASS_TYPE_GSON)) {log.info("used gson");return new GsonStrategy();} else {log.error("gson not found");throw new RuntimeException("未找到gson的依赖");}default:log.error("未找到jackson、gson或fastjson的依赖");throw new RuntimeException("未找到jackson、gson或fastjson的依赖");}}public static JsonStrategy getStrategy() {if (strategy == null) {synchronized (JsonContext.class) {if (strategy == null) {strategy = loadFromConfig();}}}return strategy;}private void setStrategy(JsonStrategy strategy) {this.strategy = strategy;}}

外部Client程序只会用到JsonContext类和JsonStrategy引用,不用用到具体的JsonStrategy实现类,实现了client和json开源组件的解耦,达到高内聚低耦合的效果。JsonUtil类需要进行完善一下,具体代码如下:


public class JsonUtil {//序列化: pojo =》 json 字符串//使用策略模式 将 POJO 转成字符串public static String pojoToJson(Object obj) {JsonStrategy strategy = JsonContext.getStrategy();String json = strategy.toJson(obj);return json;}//反序列化:json 字符串 =》  pojo//使用策略模式 将 字符串 转成POJOpublic static <T> T jsonToPojo(String json, Class<T> tClass) {JsonStrategy strategy = JsonContext.getStrategy();T t = strategy.fromJson(json, tClass);return t;}public static  <K, V> Map<K, V> jsonToMap(String json, Type type) {JsonStrategy strategy = JsonContext.getStrategy();Map<K, V> t = strategy.toMap(json,type);return t;}}

Protobuf

ProtobufProtocol Buffers 的简称,它是一种由Google开发的跨平台、语言无关、可扩展的序列化二进制数据格式。

与 JSON 类似,Protocol Buffers 也用于在不同系统、不同语言之间进行数据交换和存储。它通过定义消息结构(.proto 文件)来描述数据的类型和格式,并使用专门的编译器(例如protoc)来生成针对特定编程语言的序列化和反序列化代码。

与JSON 、 XML 相比, Protobuf 算是后起之秀,只是 Protobuf 更加适合于高性能、快速响 应的数据传输应用场景。 Protobuf 数据包是一种二进制的格式,相对于文本格式的数据交换 JSON 、 XML )来说,速度要快很多 。由于 Protobuf 优异的性能,使得它更加适用于分布式应用场景下的数据通信或者异构环境下的数据交换。

Protocol Buffers 具有以下特点:

  • 高效性:Protocol Buffers 采用二进制编码,相比于文本格式如 JSON,它能够更高效地进行数据压缩和传输,占用更少的存储空间和带宽。
  • 可扩展性:Protocol Buffers 提供了向后兼容和字段标签等特性,可以方便地进行数据模型的演进和版本管理。
  • 跨平台、跨语言:Protocol Buffers 的定义文件可以在不同的编程语言中使用,生成的序列化和反序列化代码也支持跨平台操作。
  • 性能优化:Protocol Buffers 生成的代码通常比手动编写的序列化和反序列化代码更高效,能够提供更快的数据操作速度。
  • 代码生成:通过使用特定的编译器(如protoc),根据 .proto 文件可以生成用于不同编程语言的数据结构和序列化代码。

在使用 Protocol Buffers 时,需要定义消息结构的 .proto 文件,并使用对应的编译器生成目标语言的代码。然后,就可以使用生成的代码进行消息的序列化和反序列化操作,实现跨平台、跨语言的数据交换。

proto文件生成

Protobuf使用 proto 文件来预先定义的消息格式。数据包是按照 proto 文件所定义的消息格式完成二进制码流的编码和解码。

proto 文件就是一个消息的协议文件,这个协议文件的后缀文件名为“.proto ”。其目录结构如下:
在这里插入图片描述

Msg.proto文件具体内容如下:

// [开始声明]
syntax = "proto3";//定义protobuf的包名称空间
package com.th.protocol;
// [结束声明]// [开始 java 选项配置]
//作用:在生成 proto ”文件中消息的POJO 类和 Builder (构造者)的 Java 代码时,将生成的 Java 代码放入该选项所指定的 package类路径中。
option java_package = "com.th.protocol";
//作用:在生成 proto件所对应 Java代码时,生产的 Java 外部类使用配置的名称。
option java_outer_classname = "MsgProtos";
// [结束 java 选项配置]// [开始 消息定义]
//message 关键字来定义消息的结构体。
//每一个消息结构体可以有多个字段。定义一个字段的格式为“类型名称= 编号”。
message Msg {uint32 id = 1;  // Unique ID number for this person.string content = 2;
}
message Msg2 {uint32 id = 1;  // Unique ID number for this person.string content = 2;
}
message Msg3 {uint32 id = 1;  // Unique ID number for this person.string content = 2;
}
// [结束 消息定义]

在每一个“.proto ”文件中,可以声明多个 message 。大部分情况下会把存在依赖关 系或者包含关系的 message 消息结构体写入一个 .proto 文件。将那些没有关系、相互独立的 message 消息结构体,分别写入不同的文件,这样便于管理。

完成 “.proto ”文件定义后,下一步就是生成消息的 POJO 类和 Builder (构造者)类。有两种方式生成 Java 类:

(1)通过控制台命令的方式;

(2)使用 Maven 插件的方式(推荐使用)。

使用maven插件

首先从“https://github.com/protocolbuffers/protobuf/releases ”下载 Protobuf 的安装包,可以选择不同的版本,这里下载的是 3.6.1 的 Java 版本。

使用protobuf maven plugin 插件,可以非常方便地生成消息的 POJO 类和 Builder 类的 Java 代码。在 Maven 的 pom 文件中增加此 plugin 插件的配置项,具体如下:

 <plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.5</version><extensions>true</extensions><configuration><!--proto文件路径--><protoSourceRoot>${project.basedir}/protobuf</protoSourceRoot><!--目标路径--><outputDirectory>${project.build.sourceDirectory}</outputDirectory><!--设置是否在生成java文件之前清空outputDirectory的文件--><clearOutputDirectory>false</clearOutputDirectory><!--临时目录--><temporaryProtoFileDirectory>${project.build.directory}/protoc-temp</temporaryProtoFileDirectory><!--protoc 可执行文件路径--><protocExecutable>${project.basedir}/protobuf/protoc3.6.1.exe</protocExecutable></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin>

protobuf-maven-plugin 插件的配置项,具体介绍如下:

  • protoSourceRoot proto” 消息结构体所在文件的路径;
  • outputDirectory :生成的 POJO 类和 Builder 类的目标路径;
  • protocExecutable :protobuf 的 Java 代码生成工具的 protoc3.6.1.exe 可执行文件的 路径。

配置好之后,执行插件的compile 命令, Java 代码就利索生成了。

在这里插入图片描述

演示示例

添加maven依赖:

<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>3.6.1</version>
</dependency>

使用Builder 构造POJO消息对象

   public static MsgProtos.Msg buildMsg(){MsgProtos.Msg.Builder personBuilder = MsgProtos.Msg.newBuilder();personBuilder.setId(1000);personBuilder.setContent("protoBuf适用于分布式系统");MsgProtos.Msg message = personBuilder.build();return message;}

获得消息POJO 的实例之后,可以通过多种方法将 POJO 对象序列化成二进制字节,或者反序列化。

方式一:调用 Protobuf POJO 对象的 toByteArray() 方法将 POJO 对象序列化成字节数组,具体的代码如下:

  //第1种方式:序列化 serialization & 反序列化 Deserialization@Testpublic void serAndDesr1() throws IOException{MsgProtos.Msg message = buildMsg(1,"protoBuf适用于高并发、高性能的分布式系统");//将Protobuf对象,序列化成二进制字节数组byte[] data = message.toByteArray();//可以用于网络传输,保存到内存或外存ByteArrayOutputStream outputStream = new ByteArrayOutputStream();outputStream.write(data);data = outputStream.toByteArray();//二进制字节数组,反序列化成Protobuf 对象MsgProtos.Msg inMsg = MsgProtos.Msg.parseFrom(data);Logger.info("devId:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}

这种方式类似于普通Java 对象的序列化,适用于很多将 Protobuf 的 POJO 序列化到内存或 者外存(如物理硬盘)的应用场景。

方式二:通过调用Protobuf 生成的 POJO 对象的 writeTo (OutputStream )方法将 POJO 对象 的二进制字节写出到输出流。通过调用 Protobuf 生成的 POJO 对象的 parseFrom( InputStream) 方法, Protobuf 从输入流中读取二进制码然后反序列化,得到 POJO 新的实例。具体的代码如下:

//第2种方式:序列化 serialization & 反序列化 Deserialization@Testpublic void serAndDesr2() throws IOException{MsgProtos.Msg message = buildMsg();//序列化到二进制流ByteArrayOutputStream outputStream = new ByteArrayOutputStream();message.writeTo(outputStream);ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());//从二进流,反序列化成Protobuf 对象MsgProtos.Msg inMsg = MsgProtos.Msg.parseFrom(inputStream);Logger.info("devId:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}

在阻塞式的二进制码流传输应用场景中,这种序列化和反序列化的方式是没有问题的。 例如,可以将二进制码流写入阻塞式的 Java OIO 套接字或者输出到文件。但是,这种方式在 异步操作的 NIO 应用场景中,存在粘包半包的问题。

方式三:通过调用Protobuf 生成的 POJO 对象的 writeDelimitedTo( OutputStream )方法在序列化的字节码之前添加了字节数组的长度。

 //第3种方式:序列化 serialization & 反序列化 Deserialization//带字节长度:[字节长度][字节数据],解决粘包问题@Testpublic void serAndDesr3() throws IOException{MsgProtos.Msg message = buildMsg();//序列化到二进制流ByteArrayOutputStream outputStream = new ByteArrayOutputStream();message.writeDelimitedTo(outputStream);ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());//从二进流,反序列化成Protobuf 对象MsgProtos.Msg inMsg = MsgProtos.Msg.parseDelimitedFrom(inputStream);Logger.info("devId:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}

这种方式可以用于异步操作的NIO 应用场景中,解决了粘包半包的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135948.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端VUE---JS实现数据的模糊搜索

实现背景 因为后端实现人员列表返回&#xff0c;每次返回的数据量在100以内&#xff0c;要求前端自己进行模糊搜索 页面实现 因为是实时更新数据的&#xff0c;就不需要搜索和重置按钮了 代码 HTML <el-dialogtitle"团队人员详情":visible.sync"centerDi…

uni-app跳转到另一个app

第一步&#xff1a; 首先要知道 app的包名 获取方式如下 第二步&#xff1a; 在第一个 demo1 app 一个页面中需要一个按钮去跳转 方法如下 <template><view class"content"><button click"tz">跳转</button></view> </…

如何在微软Edge浏览器上一键观看高清视频?

编者按&#xff1a;视频是当下最流行的媒体形式之一。但由于视频压缩、网络不稳定等原因&#xff0c;我们常常可以看到互联网上的很多视频其画面质量并不理想&#xff0c;尤其是在浏览器端&#xff0c;这极大地影响了观看体验。不过&#xff0c;近期微软 Edge 浏览器推出了一项…

FPGA纯verilog实现8路视频拼接显示,提供工程源码和技术支持

目录 1、前言版本更新说明免责声明 2、我已有的FPGA视频拼接叠加融合方案3、设计思路框架视频源选择OV5640摄像头配置及采集静态彩条视频拼接算法图像缓存视频输出 4、vivado工程详解5、工程移植说明vivado版本不一致处理FPGA型号不一致处理其他注意事项 6、上板调试验证并演示…

Jmeter接口测试简易步骤

使用Jmeter接口测试 1、首先右键添加一个线程组&#xff0c;然后我们重命名接口测试 2、在线程组上添加一个Http默认请求&#xff0c;并配置服务器的IP地址端口等信息 3、在线程组中添加一个HTTP请求&#xff0c;这里我们重命名“增加信用卡账户信息接口” 4、配置接口请求信息…

使用延迟队列解决分布式事务问题——以订单未支付过期,解锁库存为例

目录 一、前言 二、库存 三、订单 一、前言 上一篇使用springcloud-seata解决分布式事务问题-2PC模式我们说到了使用springcloud-seata解决分布式的缺点——不适用于高并发场景 因此我们使用延迟队列来解决分布式事务问题&#xff0c;即使用柔性事务-可靠消息-最终一致性方…

Kotlin simple convert ArrayList CopyOnWriteArrayList MutableList

Kotlin simple convert ArrayList CopyOnWriteArrayList MutableList Kotlin读写分离CopyOnWriteArrayList_zhangphil的博客-CSDN博客Java并发多线程环境中&#xff0c;造成死锁的最简单的场景是&#xff1a;多线程中的一个线程T_A持有锁L1并且申请试图获得锁L2&#xff0c;而多…

Redis缓存实现及其常见问题解决方案

随着互联网技术的发展&#xff0c;数据处理的速度和效率成为了衡量一个系统性能的重要指标。在众多的数据处理技术中&#xff0c;缓存技术以其出色的性能优化效果&#xff0c;成为了不可或缺的一环。而在众多的缓存技术中&#xff0c;Redis 以其出色的性能和丰富的功能&#xf…

flutter开发实战-长按TextField输入框cut、copy设置为中文复制、粘贴

flutter开发实战-长按TextField输入框cut、copy设置为中文复制、粘贴 在开发过程中&#xff0c;需要长按TextField输入框cut、copy设置为中文“复制、粘贴”&#xff0c;这里记录一下设置的代码。 一、pubspec.yaml设置flutter_localizations 在pubspec.yaml中设置flutter_l…

23下半年学习计划

大二上学期计划 现在已经是大二了&#xff0c;java只学了些皮毛&#xff0c;要学的知识还有很多&#xff0c;新的学期要找准方向&#xff0c;把要学的知识罗列&#xff0c;按部就班地完成计划&#xff0c;合理安排时间&#xff0c;按时完成学习任务。 学习node.js&#xff0c…

企业架构LNMP学习笔记48

数据结构类型操作&#xff1a; 数据结构&#xff1a;存储数据的方式 数据类型 算法&#xff1a;取数据的方式&#xff0c;代码就把数据进行组合&#xff0c;计算、存储、取出。 排序算法&#xff1a;冒泡排序、堆排序 二分。 key&#xff1a; key的命名规则不同于一般语言…

Android 12 源码分析 —— 应用层 六(StatusBar的UI创建和初始化)

Android 12 源码分析 —— 应用层 六&#xff08;StatusBar的UI创建和初始化) 在前面的文章中,我们分别介绍了Layout整体布局,以及StatusBar类的初始化.前者介绍了整体上面的布局,后者介绍了三大窗口的创建的入口处,以及需要做的准备工作.现在我们分别来细化三大窗口的UI创建和…

GitLab使用的最简便方式

GitLab介绍 GitLab是一个基于Git版本控制系统的开源平台&#xff0c;用于代码托管&#xff0c;持续集成&#xff0c;以及协作开发。它提供了一套完整的工具&#xff0c;以帮助开发团队协同工作、管理和部署代码。 往往在企业内部使用gitlab管理代码&#xff0c;记录一下将本地代…

redis 集群(cluster)

1. 前言 我们知道&#xff0c;在Web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999% 等等&#xff09;。但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xf…

6.3 字符数组

思维导图&#xff1a; 前言&#xff1a; 主要内容&#xff1a; 前言内容整理 字符型数据和存储 字符型数据是依据字符的ASCII代码存储在内存单元中&#xff0c;通常占用一个字节的空间。ASCII代码可以被认为是整数&#xff0c;因此在C99标准中&#xff0c;字符类型被归类为整…

现在进入广告行业好做吗?

广告行业真的很好&#xff0c;大家快来…… 在这里你可以无限发挥你的创意和想象力&#xff0c;有趣的同事&#xff0c;不刻板的工作内容&#xff0c;与爱豆合作&#xff0c;偶尔见见明星&#xff0c;出入城市CBD&#xff0c;一身名牌&#xff0c;精美PPT挥斥方遒&#xff0c;…

渗透测试之漏洞挖掘指南(一)

1.漏洞挖掘中什么漏洞最多&#xff1f; 新手想快速挖掘到漏洞&#xff0c;要专注在业务逻辑与前端漏洞 -- 业务逻辑 &#xff08;弱密码&#xff0c;等等&#xff09; -- 前端漏洞 &#xff08;xss, csrf , cors, jsonp...&#xff09; 2. 常见漏洞提交平台 注册应急响应中…

数据清洗:数据挖掘的前期准备工作

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

【unity小技巧】Unity 存储存档保存——PlayerPrefs、JsonUtility和MySQL数据库的使用

文章目录 前言PlayerPrefs一、基本介绍二、Demo三、优缺点 JsonUtility一、基本使用二、Demo三、优缺点 Mysql&#xff08;扩展&#xff09;完结 前言 游戏存档不言而喻&#xff0c;是游戏设计中的重要元素&#xff0c;可以提高游戏的可玩性&#xff0c;为玩家提供更多的自由和…

更新GitLab上的项目

更新GitLab上的项目 如有需要&#xff0c;请参考这篇&#xff1a;上传项目到gitlab上 1.打开终端&#xff0c;进入到本地项目的根目录。 2.如果你还没有将远程GitLab仓库添加到本地项目&#xff0c;你可以使用以下命令&#xff1a; 比如&#xff1a; git remote add origin …