序列化方式二——JSON之Gson
Gson
1、什么是Gson?
Gson是Google提供的一个用于Java编程语言的JSON(JavaScript Object Notation)序列化和反序列化库。它允许开发者在Java对象和JSON数据之间进行高效的映射和转换。
官网地址:https://github.com/google/gson
官网文档:https://google.github.io/gson/UserGuide.html
2、为什么选择Gson?
-
快速、高效:Gson采用了一些优化策略,确保在处理大量数据时能够保持高效率。
-
代码量少、简洁:Gson的设计简洁,使用起来非常方便,开发者可以以较少的代码实现复杂的JSON处理功能。
-
面向对象:Gson支持Java对象的序列化,能够将复杂的Java对象(包括嵌套对象和集合)转换为JSON字符串,同时也支持将JSON字符串反序列化为Java对象。
-
数据传递和解析方便:Gson提供了灵活的数据传递和解析方式,使得在前后端之间传递数据时变得非常方便。
-
自定义序列化和反序列化:Gson允许开发者通过实现JsonSerializer和JsonDeserializer接口来自定义对象的序列化和反序列化过程,以满足特定需求。
-
支持复杂对象:Gson能够处理复杂的Java对象,包括嵌套对象和集合,它会自动处理对象之间的关系,将它们转换为相应的JSON格式。
-
与Android兼容:Gson在Android开发中也被广泛使用,因为它与Android平台兼容,能够方便地处理Android应用中的JSON数据。
-
性能优化:对于大型数据集,Gson提供了性能优化的选项,如使用JsonReader和JsonWriter进行流式处理,以减少内存占用并提高处理速度。
-
版本控制:Gson支持使用@Since和@Until注解来指定字段的版本信息,以便在对象模型发生变化时保持向后和向前兼容性。
-
处理空值和异常:Gson能够处理Java对象中的null值,并将它们序列化为JSON字符串中的null。同时,Gson也提供了异常处理机制,以便在反序列化过程中捕获和处理可能出现的异常。
3、Gson常用的API
3.1. Gson对象的创建
-
直接创建:通过
new Gson()
直接创建一个Gson对象,使用默认配置。 -
GsonBuilder:使用
GsonBuilder
类可以构建自定义配置的Gson对象,如设置日期格式、排除空值、自定义序列化器等。
3.2. 序列化
- toJson()方法:将Java对象序列化为JSON字符串。Gson提供了多个重载的
toJson()
方法,支持将对象、对象数组、集合等转换为JSON字符串。
3.3. 反序列化
- fromJson()方法:将JSON字符串反序列化为Java对象。Gson同样提供了多个重载的
fromJson()
方法,支持将JSON字符串解析为指定的Java对象、对象数组、集合等。
3.4. JsonElement、JsonObject和JsonArray
-
JsonElement:表示JSON树中的一个节点,可以是JsonObject、JsonArray、JsonPrimitive或JsonNull。
-
JsonObject:表示JSON对象,即键值对集合。可以通过
get()
方法获取特定键对应的值,使用put()
方法添加或修改键值对。 -
JsonArray:表示JSON数组,即值的有序列表。可以使用
get()
方法按索引访问数组中的元素,使用add()
方法添加元素。
3.5. TypeToken
- 当需要将JSON字符串反序列化为泛型集合时,由于类型擦除,直接使用
fromJson()
方法会丢失泛型信息。此时,可以使用TypeToken
类来捕获泛型信息。
3.6. 自定义序列化器与反序列化器
-
JsonSerializer和JsonDeserializer:Gson 1.x版本中提供的自定义序列化器和反序列化器接口,基于树型结构进行解析。
-
TypeAdapter:Gson 2.0版本中新增的自定义序列化器和反序列化器接口,基于流式结构进行解析,相比树型结构更节省内存。
3.7. 注解
Gson提供了一系列注解来支持复杂的序列化和反序列化场景,如:
-
@SerializedName:用于指定JSON属性名与Java字段名之间的映射关系。
-
@Expose:用于控制字段是否参与序列化和反序列化。
-
@JsonAdapter:用于指定字段或类使用自定义的序列化器或反序列化器。
-
@Since和@Until:用于控制字段的序列化版本。
3.8. 流式API
对于大量数据,Gson提供了流式API,即JsonReader
和JsonWriter
,可以有效地读写JSON数据,减少内存占用。
4、使用
4.1、引入依赖
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.10.1</version>
</dependency>
4.2、简单示例(json->对象,对象->json)
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
@Test
public void test() {// 创建Java对象GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 创建Gson对象Gson gson = new Gson();// 序列化:将Java对象转换为JSON字符串(对象->json)String json = gson.toJson(gsonStudent);System.out.println(json); // 输出: {"name":"Alice","age":30}// 反序列化:将JSON字符串转换为Java对象(json->对象)GsonStudent newPerson = gson.fromJson(json, GsonStudent.class);System.out.println(newPerson.getName()); // 输出: AliceSystem.out.println(newPerson.getAge()); // 输出: 30
}
4.3、 自定义序列化器与反序列化器
通过实现JsonSerializer和JsonDeserializer接口来自定义序列化和反序列化过程。这在你需要处理复杂对象或特殊格式时非常有用。
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
序列化器
package com.zhz.test.json.gson;import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.zhz.test.entity.GsonStudent;import java.lang.reflect.Type;public class GsonStudentSerializer implements JsonSerializer<GsonStudent> {@Overridepublic JsonElement serialize(GsonStudent src, Type typeOfSrc, JsonSerializationContext context) {JsonObject jsonObject = new JsonObject();jsonObject.addProperty("name", src.getName());// 假设我们对年龄进行特殊处理,比如加10jsonObject.addProperty("age", src.getAge() + 10);// 可以添加更多自定义逻辑return jsonObject;}
}
反序列化器
package com.zhz.test.json.gson;import com.google.gson.*;
import com.zhz.test.entity.GsonStudent;import java.lang.reflect.Type;public class GsonStudentDeserializer implements JsonDeserializer<GsonStudent> {@Overridepublic GsonStudent deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {JsonObject jsonObject = json.getAsJsonObject();String name = jsonObject.get("name").getAsString();// 假设我们从JSON中读取的年龄需要减10以还原原始值int age = jsonObject.get("age").getAsInt() - 10;// 创建并返回Person对象return GsonStudent.builder().name(name).age(age).build();}
}
测试代码
@Testpublic void testSerializable() {// 创建并注册自定义序列化器和反序列化器Gson gson = new GsonBuilder().registerTypeAdapter(GsonStudent.class, new GsonStudentSerializer()).registerTypeAdapter(GsonStudent.class, new GsonStudentDeserializer()).create();// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();String json = gson.toJson(gsonStudent);System.out.println(json); // 输出: {"name":"John","age":40}// 反序列化GsonStudent deserializedPerson = gson.fromJson(json, GsonStudent.class);System.out.println(deserializedPerson); // 输出: Person{name='John', age=20}}
4.4、排除字段
-
使用@Expose注解来标记哪些字段应该被序列化或反序列化。未标记的字段将默认被排除。
-
在Gson中,如果你想要排除某些字段,在序列化过程中不被包含到JSON字符串中,有几种方法可以实现这一点。然而,Gson本身并没有直接提供一个简单的注解来标记一个字段应该被排除。不过,你可以使用@Expose注解(虽然它主要用于Gson 2.x之前的版本,并且在新版本中可能不再推荐使用),或者更常见的是,使用GsonBuilder的excludeFieldsWithoutExposeAnnotation()方法结合@Expose注解(尽管这是标记哪些字段应该被包含,而不是排除),或者使用@SerializedName注解的变体(实际上不是用于排除,但可以用来避免生成JSON中的某些字段),或者通过自定义序列化器来完全控制序列化过程。
-
不过,对于Gson 2.8及以上版本,简单地通过自定义序列化器来排除字段。以下是一个使用自定义序列化器来排除字段的示例:
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
/*** 排除字段*/
@Test
public void testExclusion(){// 创建一个Gson实例,使用自定义的排除策略Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {@Overridepublic boolean shouldSkipField(FieldAttributes f) {// 这里可以根据字段名、类型等条件来排除字段// 例如,排除所有名为"age"的字段return "age".equals(f.getName());}@Overridepublic boolean shouldSkipClass(Class<?> clazz) {// 这里可以根据类来排除整个类的序列化,但通常不需要return false;}}).create();// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 序列化Person对象,注意"age"字段被排除了String json = gson.toJson(gsonStudent);System.out.println(json); // 输出可能类似于: {"name":"John"}
}
4.5、格式化输出
使用GsonBuilder的setPrettyPrinting()方法可以使Gson以更易读的格式输出JSON字符串。
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
@Test
public void testPrettyPrinting(){// 使用GsonBuilder设置格式化输出并创建Gson对象Gson gson = new GsonBuilder().setPrettyPrinting().create();GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 将Person对象序列化为格式化的JSON字符串String jsonString = gson.toJson(gsonStudent);// 输出结果System.out.println(jsonString);
}
4.6、处理泛型
对于泛型集合,Gson提供了TypeToken来捕获泛型信息,以便正确地进行反序列化。
基本对象
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
处理List集合
/*** 测试泛型*/@Testpublic void testPansexual(){// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 创建一个包含Person对象的ListList<GsonStudent> gsonStudents = new ArrayList<>();gsonStudents.add(gsonStudent);// 创建Gson实例Gson gson = new Gson();// 序列化List<Person>为JSON字符串String json = gson.toJson(gsonStudents);System.out.println(json);// 反序列化JSON字符串回List<Person>// 注意:由于类型擦除,我们需要使用TypeToken来捕获List<Person>的类型信息Type listType = new TypeToken<List<GsonStudent>>(){}.getType();List<GsonStudent> deserializedPersons = gson.fromJson(json, listType);// 输出反序列化后的List内容for (GsonStudent gsonStudent1 : deserializedPersons) {System.out.println(gsonStudent1);}}
处理Set
/*** 测试Set泛型*/@Testpublic void testPansexualSet(){// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 创建一个包含Person对象的ListSet<GsonStudent> gsonStudents = new HashSet<>();gsonStudents.add(gsonStudent);// 创建Gson实例Gson gson = new Gson();// 序列化List<Person>为JSON字符串String json = gson.toJson(gsonStudents);System.out.println(json);// 反序列化JSON字符串回List<Person>// 注意:由于类型擦除,我们需要使用TypeToken来捕获List<Person>的类型信息Type setType = new TypeToken<Set<GsonStudent>>(){}.getType();Set<GsonStudent> deserializedPersons = gson.fromJson(json, setType);// 输出反序列化后的List内容for (GsonStudent gsonStudent1 : deserializedPersons) {System.out.println(gsonStudent1);}}
处理Map
/*** 测试Map泛型*/@Testpublic void testPansexualMap(){// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 创建一个包含Person对象的ListMap<String,GsonStudent> gsonStudents = new HashMap<>();gsonStudents.put("111",gsonStudent);// 创建Gson实例Gson gson = new Gson();// 序列化List<Person>为JSON字符串String json = gson.toJson(gsonStudents);System.out.println(json);// 反序列化JSON字符串回List<Person>// 注意:由于类型擦除,我们需要使用TypeToken来捕获List<Person>的类型信息Type mapType = new TypeToken<Map<String,GsonStudent>>(){}.getType();Map<String,GsonStudent> deserializedPersons = gson.fromJson(json, mapType);// 输出反序列化后的Map内容for (Map.Entry<String, GsonStudent> entry : deserializedPersons.entrySet()) {System.out.println(entry.getKey()+"\t"+entry.getValue());}}
4.7、处理日期格式
Gson本身并不直接提供注解来指定日期格式,但你可以通过自定义序列化器(JsonSerializer
)和反序列化器(JsonDeserializer
)来解决这个问题。
以下是一个处理日期格式的示例,展示了如何为java.util.Date
类型自定义序列化器和反序列化器,并设置特定的日期格式(例如,yyyy-MM-dd
)。代码如下:
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
自定义Date序列化
package com.zhz.test.json.gson;import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateSerializer implements JsonSerializer<Date> {private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");@Overridepublic JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {return new JsonPrimitive(dateFormat.format(src));}
}
自定义Date反序列化器
package com.zhz.test.json.gson;import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;import java.lang.reflect.Type;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateDeserializer implements JsonDeserializer<Date> {private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");@Overridepublic Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {try {return dateFormat.parse(json.getAsString());} catch (ParseException e) {throw new JsonParseException(e);}}
}
测试demo
@Test
public void testDate(){Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new DateSerializer()).registerTypeAdapter(Date.class, new DateDeserializer()).create();// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 序列化对象String json = gson.toJson(gsonStudent);System.out.println(json); // 输出类似:{"date":"2023-04-01"}// 反序列化JSON字符串GsonStudent deserializedObj = gson.fromJson(json, GsonStudent.class);System.out.println(deserializedObj.getBirthday()); // 输出:对应的Date对象,但注意它只包含日期部分
}
4.8、处理异常
在使用Gson库处理JSON数据时,可能会遇到各种异常情况,例如解析错误、类型转换错误等。Gson通过抛出异常来指示这些错误情况,因此你需要适当地捕获和处理这些异常。Gson主要会抛出以下几种类型的异常:
-
JsonSyntaxException:当输入的JSON字符串不符合JSON规范时,Gson会抛出此异常。这通常发生在解析JSON数据时。
-
JsonParseException:Gson在解析JSON时,如果遇到无法理解的格式或结构,也会抛出此异常。虽然Gson主要使用
JsonSyntaxException
,但在某些情况下(或旧版本中),你可能会遇到JsonParseException
。 -
JsonIOException:当Gson在读取或写入JSON数据时遇到I/O错误时,会抛出此异常。这通常与文件或网络I/O操作相关。
-
IllegalStateException:虽然这不是Gson特有的异常,但在Gson的使用中,如果Gson的状态不正确(比如,在反序列化过程中尝试修改JsonReader的状态),则可能会抛出此异常。
-
TypeMismatchException(Gson 2.8.6及以后版本):这是一个自定义的异常,用于指示在反序列化过程中类型不匹配的情况。它可以帮助你更容易地识别和处理类型错误。
-
IllegalArgumentException 和 NullPointerException:这些Java标准异常也可能在Gson的使用中出现,尤其是在处理无效输入或配置错误时。
解决方案,只需要在fromJson ,toJson的时候加个try/catch就好了
@Test
public void testTryCatch() {// 创建Java对象GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();// 创建Gson对象Gson gson = new Gson();// 序列化:将Java对象转换为JSON字符串String json = gson.toJson(gsonStudent);System.out.println(json); // 输出: {"name":"Alice","age":30}try {// 反序列化:将JSON字符串转换为Java对象GsonStudent newPerson = gson.fromJson(json, GsonStudent.class);System.out.println(newPerson.getName()); // 输出: AliceSystem.out.println(newPerson.getAge()); // 输出: 30}catch (Exception e) {throw new RuntimeException(e);}
}
4.9、字段访问权限
Gson默认只能访问类中的公共字段(public fields)和方法。如果类变量的访问权限不是公共的(如private、protected或默认包级私有),Gson就无法直接访问到这些变量。为了解决这个问题,Gson提供了几种方法:
-
使用@Expose注解:通过@Expose注解,可以将非公共字段标记为可序列化和反序列化的字段。但请注意,要使用这个注解,必须在创建Gson对象时通过GsonBuilder配置Gson实例,以包含@Expose注解的字段。
-
自定义序列化器和反序列化器:通过实现JsonSerializer和JsonDeserializer接口,可以自定义序列化和反序列化的逻辑,从而控制对字段的访问。
-
修改字段访问权限:虽然这不是最佳实践,但在某些情况下,将字段的访问权限修改为public可能是一个简单的解决方案。
4.10、循环引用
循环引用是指两个或多个对象之间相互引用的现象。在Java中,循环引用可能导致内存泄漏和程序崩溃等问题。Gson提供了处理循环引用的方法:
-
自动处理:在大多数情况下,Gson能够自动处理循环引用,确保数据结构的稳定性。这通常是通过在序列化过程中检测并处理循环引用来实现的。
-
自定义序列化器和反序列化器:对于复杂的循环引用情况,可以通过自定义序列化器和反序列化器来特别处理循环引用,如跳过循环引用对象的某些属性等。
-
使用GsonBuilder的setExclusionStrategies:通过实现ExclusionStrategy接口,可以定义排除策略,从而在序列化或反序列化过程中排除特定的字段或类。
4.11、性能优化&TypeAdapter测试用例
Gson的性能优化可以从多个方面入手:
-
全局Gson对象:在项目中提供一个全局的Gson对象,以避免频繁创建Gson实例带来的性能开销。
-
避免不必要的序列化:只序列化需要的数据,避免序列化整个对象图,特别是包含大量不必要数据或复杂对象图的情况。
-
自定义序列化器和反序列化器:通过自定义序列化器和反序列化器,可以优化序列化和反序列化的过程,减少不必要的计算或内存使用。
-
使用流式API(TypeAdapter):Gson 2.0引入了TypeAdapter,这是一个基于流式结构的API,相比基于树型结构的JsonSerializer和JsonDeserializer,TypeAdapter更节省内存。\
下面给一个TypeAdapter的测试用例
package com.zhz.test.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GsonStudent {private String name;private String sex;private Integer age;private Date birthday;
}
package com.zhz.test.json.gson;import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.zhz.test.entity.GsonStudent;import java.io.IOException;public class GsonStudentTypeAdapter extends TypeAdapter<GsonStudent> {@Overridepublic void write(JsonWriter out, GsonStudent value) throws IOException {if (value == null) {out.nullValue();return;}out.beginObject();out.name("name").value(value.getName());out.name("age").value(value.getAge());out.endObject();}@Overridepublic GsonStudent read(JsonReader in) throws IOException {if (in.peek() == JsonToken.NULL) {in.nextNull();return null;}in.beginObject();String name = null;int age = 0;while (in.hasNext()) {switch (in.nextName()) {case "name":name = in.nextString();break;case "age":age = in.nextInt();break;default:in.skipValue(); // 忽略未知字段}}in.endObject();return GsonStudent.builder().age(age).name(name).build();}
}
@Test
public void testSerializationAndDeserialization() {// 序列化GsonStudent gsonStudent = GsonStudent.builder().age(18).sex("男").birthday(new Date()).name("张三").build();Gson gson = new GsonBuilder().registerTypeAdapter(GsonStudent.class, new GsonStudentTypeAdapter()).create();// 序列化String json = gson.toJson(gsonStudent);System.out.println(json);// 反序列化GsonStudent deserializedUser = gson.fromJson(json, GsonStudent.class);System.out.println(deserializedUser);
}
4.12、版本控制
Gson的版本控制主要涉及到两个方面:
-
Gson库的版本:在使用Gson时,应关注Gson库的最新版本,以便利用新功能和性能改进。同时,也需要注意版本兼容性,避免升级后引入不兼容的变更。
-
序列化数据的版本:在序列化数据时,有时需要控制数据的版本,以便在反序列化时能够处理不同版本的数据。Gson提供了@Since和@Until注解,用于声明字段的起始和终止序列化版本。这样,在反序列化时,只有满足版本条件的字段才会被包含在内。
5、注意事项
-
版本兼容性:Gson的不同版本之间可能存在一些差异,特别是API的变化。因此,在升级Gson版本时,请务必参考官方文档或更新日志。
-
性能优化:对于大型数据集,Gson提供了流式API(
JsonReader
和JsonWriter
)来减少内存占用并提高性能。 -
安全性:在处理不受信任的JSON数据时,请确保你的应用程序能够正确处理潜在的安全问题,如JSON注入攻击。
打个广告
本人新搞的个人项目,有意者可到 DDD用户中台 这里购买
可以学习到的体系
-
项目完全从0到1开始架构,包含前端,后端,架构,服务器,技术管理相关运维知识!
- 最佳包名设计,项目分层
-
破冰CRUD,手撕中间件!
-
基于MybatisPlus封装属于自己的DDD ORM框架
-
基于Easyexcel封装属于自己的导入导出组件
-
oss对象存储脚手架(阿里云,minio,腾讯云,七牛云等)
-
邮件脚手架
-
completefuture脚手架
-
redis脚手架
-
xxl-job脚手架
-
短信脚手架
-
常用工具类等
-
-
传统MVC代码架构弊端的解决方案
- DDD+CQRS+ES最难架构
-
结合实际代码的业务场景
-
多租户单点登录中心
-
用户中台
-
消息中心
-
配置中心
-
监控设计
-
-
程序员的职业规划,人生规划
-
打工永远没有出路!
-
打破程序员的35岁魔咒
-
技术带给你的优势和竞争力【启发】
-
万物互联网的淘金之路!
-
技术以外的赚钱路子
可以一起沟通
具体的文章目录
购买链接
DDD用户中台