码字不易,请大佬们点点关注,谢谢~
一、Gson注解概述
1.1 注解在Gson中的作用
在Android开发中,Gson作为主流的JSON处理库,通过注解机制提供了灵活的对象序列化和反序列化规则。注解允许开发者直接在Java类的字段上声明JSON转换规则,而无需编写额外的适配器代码。这种方式简化了开发流程,提高了代码的可读性和可维护性。
Gson提供的核心注解包括:
@SerializedName
:指定JSON字段名称@Expose
:控制字段的序列化和反序列化@Since
和@Until
:版本控制@JsonAdapter
:指定自定义TypeAdapter
这些注解通过与Gson的反射机制结合,实现了基于注解的转换规则。
1.2 注解处理的基本流程
Gson处理注解的基本流程如下:
- 反射获取字段信息:Gson通过Java反射机制获取类的所有字段
- 注解解析:检查每个字段上的注解信息
- 规则应用:根据注解信息修改默认的序列化和反序列化行为
- 生成JSON:根据应用的规则生成或解析JSON数据
这个流程在Gson内部通过多个组件协同完成,包括FieldNamingStrategy
、ExclusionStrategy
和反射适配器等。
二、@SerializedName注解的原理
2.1 @SerializedName注解的定义与作用
@SerializedName
是Gson中最常用的注解之一,用于指定Java字段在JSON中对应的名称。其定义如下:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
/**
* 指定JSON字段的名称
*/
String value();
/**
* 备选名称,用于反序列化时匹配多个可能的JSON字段名
*/
String[] alternate() default {};
}
通过@SerializedName
注解,开发者可以解决Java字段名与JSON字段名不一致的问题,同时支持反序列化时的多个备选名称。
2.2 @SerializedName在序列化中的处理流程
在序列化过程中,Gson通过以下步骤处理@SerializedName
注解:
- 字段扫描:通过反射获取类的所有字段
- 注解检查 :检查每个字段是否存在
@SerializedName
注解 - 名称确定:如果存在注解,则使用注解指定的名称作为JSON字段名
- 写入JSON:使用确定的名称写入JSON数据
源码分析如下:
java
// ReflectiveTypeAdapterFactory类中的关键方法
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<>();
if (raw.isInterface()) {
return result;
}
Type declaredType = type.getType();
while (raw != Object.class) {
Field[] fields = raw.getDeclaredFields();
for (Field field : fields) {
// 检查字段是否应该被序列化或反序列化
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}
// 设置字段可访问
field.setAccessible(true);
// 获取字段的类型
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
// 处理SerializedName注解
List<String> fieldNames = getFieldNames(field);
// 为字段创建BoundField
BoundField boundField = createBoundField(
context, field, fieldNames,
TypeToken.get(fieldType),
serialize,
deserialize
);
// 将BoundField添加到结果中
for (String name : fieldNames) {
BoundField previous = result.put(name, boundField);
if (previous != null) {
throw new IllegalArgumentException(
"Duplicate field name '" + name + "'. Found on " +
previous.field + " and " + field);
}
}
}
// 处理父类字段
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
// 获取字段名称的方法
private List<String> getFieldNames(Field field) {
SerializedName annotation = field.getAnnotation(SerializedName.class);
if (annotation == null) {
// 如果没有注解,使用字段名
return Collections.singletonList(fieldNamingPolicy.translateName(field));
}
// 获取主名称
String serializedName = annotation.value();
// 获取备选名称
String[] alternates = annotation.alternate();
// 构建名称列表
List<String> fieldNames = new ArrayList<>(1 + alternates.length);
fieldNames.add(serializedName);
Collections.addAll(fieldNames, alternates);
return fieldNames;
}
2.3 @SerializedName在反序列化中的处理流程
在反序列化过程中,Gson通过以下步骤处理@SerializedName
注解:
- JSON解析:解析JSON数据,获取字段名称
- 名称匹配 :将JSON字段名称与Java类字段的
@SerializedName
注解值进行匹配 - 字段赋值:如果匹配成功,则将JSON值赋给对应的Java字段
源码分析如下:
java
// ReflectiveTypeAdapterFactory.Adapter类中的read方法
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
// 创建实例
T instance = constructorConstructor.get(typeToken).construct();
try {
// 开始解析JSON对象
in.beginObject();
while (in.hasNext()) {
// 读取JSON字段名称
String name = in.nextName();
// 查找匹配的BoundField
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
// 如果没有找到或不可反序列化,则跳过
in.skipValue();
continue;
}
// 读取字段值
field.read(in, instance);
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
} finally {
// 结束解析JSON对象
in.endObject();
}
return instance;
}
2.4 备选名称(alternate)的处理机制
@SerializedName
的alternate
属性允许指定多个备选名称,用于处理JSON字段名称不一致的情况。在反序列化时,Gson会尝试所有可能的名称进行匹配。
java
// 处理备选名称的逻辑
List<String> fieldNames = getFieldNames(field);
for (String name : fieldNames) {
BoundField previous = result.put(name, boundField);
if (previous != null) {
throw new IllegalArgumentException("Duplicate field name");
}
}
在上述代码中,getFieldNames
方法返回的列表包含主名称和所有备选名称。这些名称都会被添加到boundFields
映射中,确保在反序列化时能够匹配到任何一个名称。
三、@Expose注解的原理
3.1 @Expose注解的定义与作用
@Expose
注解用于控制字段是否应该被序列化或反序列化。其定义如下:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
/**
* 指定该字段是否应该被序列化,默认为true
*/
boolean serialize() default true;
/**
* 指定该字段是否应该被反序列化,默认为true
*/
boolean deserialize() default true;
}
通过@Expose
注解,开发者可以精细控制哪些字段应该参与序列化和反序列化过程。
3.2 @Expose注解的处理机制
要使用@Expose
注解,必须在创建Gson实例时启用excludeFieldsWithoutExposeAnnotation()
选项:
java
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
当启用该选项后,Gson会检查每个字段是否有@Expose
注解,并根据注解的值决定是否序列化或反序列化该字段。
源码分析如下:
java
// Excluder类中的关键方法
@Override
public boolean excludeField(Field field, boolean serialize) {
// 检查是否启用了@Expose注解
if (serialize && serializeExclusionStrategy != null
&& serializeExclusionStrategy.shouldSkipField(field)) {
return true;
}
if (!serialize && deserializeExclusionStrategy != null
&& deserializeExclusionStrategy.shouldSkipField(field)) {
return true;
}
// 检查是否需要排除没有@Expose注解的字段
if (requireExpose) {
Expose expose = field.getAnnotation(Expose.class);
if (expose == null) {
return true;
}
return serialize ? !expose.serialize() : !expose.deserialize();
}
// 其他排除规则...
return false;
}
3.3 @Expose与默认行为的对比
当不使用@Expose
注解时,Gson默认会序列化和反序列化所有非静态、非瞬态的字段。而启用@Expose
注解后,只有明确标注了@Expose
的字段才会被处理。
例如:
java
class User {
@Expose
private String name;
private int age; // 没有@Expose注解
@Expose(serialize = false, deserialize = true)
private String password;
// 构造方法和getter/setter省略
}
使用以下方式创建Gson实例:
java
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
在这个例子中:
name
字段会被序列化和反序列化age
字段不会被处理,因为没有@Expose
注解password
字段只会被反序列化,不会被序列化
四、@Since和@Until注解的原理
4.1 版本控制注解的定义与作用
@Since
和@Until
注解用于基于版本号控制字段的序列化和反序列化。其定义如下:
java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Since {
/**
* 指定字段或类的版本号
*/
double value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Until {
/**
* 指定字段或类的版本截止号
*/
double value();
}
这两个注解通常用于API版本控制,允许在不同版本的应用中选择性地序列化或反序列化某些字段。
4.2 版本控制的实现机制
Gson通过VersionExclusionStrategy
类实现版本控制,该类实现了ExclusionStrategy
接口。
java
// VersionExclusionStrategy类的实现
private static final class VersionExclusionStrategy implements ExclusionStrategy {
private final double version;
public VersionExclusionStrategy(double version) {
this.version = version;
}
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 检查Since注解
Since since = f.getAnnotation(Since.class);
if (since != null && since.value() > version) {
return true;
}
// 检查Until注解
Until until = f.getAnnotation(Until.class);
if (until != null && until.value() <= version) {
return true;
}
return false;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
// 检查类上的Since注解
Since since = clazz.getAnnotation(Since.class);
if (since != null && since.value() > version) {
return true;
}
// 检查类上的Until注解
Until until = clazz.getAnnotation(Until.class);
if (until != null && until.value() <= version) {
return true;
}
return false;
}
}
4.3 版本控制的使用示例
在创建Gson实例时,可以通过setVersion
方法设置当前版本号:
java
Gson gson = new GsonBuilder()
.setVersion(2.0)
.create();
然后在类或字段上使用@Since
和@Until
注解:
java
class User {
private String name;
@Since(2.0)
private String email;
@Until(1.5)
private String oldField;
// 构造方法和getter/setter省略
}
在这个例子中:
- 当版本号设置为2.0时,
email
字段会被包含,而oldField
字段会被排除 - 当版本号设置为1.0时,
email
字段会被排除,oldField
字段会被包含
五、@JsonAdapter注解的原理
5.1 @JsonAdapter注解的定义与作用
@JsonAdapter
注解用于为特定类型指定自定义的TypeAdapter
、TypeAdapterFactory
或JsonSerializer/JsonDeserializer
。其定义如下:
java
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
public @interface JsonAdapter {
/**
* 指定自定义的TypeAdapter、TypeAdapterFactory或JsonSerializer/JsonDeserializer类
*/
Class<?> value();
/**
* 指定适配器是否应该被实例化并用于子类
*/
boolean nullSafe() default true;
}
5.2 @JsonAdapter注解的处理流程
Gson在处理@JsonAdapter
注解时,会执行以下步骤:
- 注解检查 :在序列化或反序列化前,检查目标类型或字段上是否存在
@JsonAdapter
注解 - 适配器实例化:根据注解中指定的类,通过反射创建适配器实例
- 适配器应用:使用创建的适配器处理序列化或反序列化操作
源码分析如下:
java
// TypeAdapters类中的获取适配器方法
public static TypeAdapter<?> getAdapter(TypeToken<?> type, JsonAdapter annotation) {
if (annotation == null) {
return null;
}
// 获取注解中指定的适配器类
Class<?> adapterClass = annotation.value();
// 检查适配器类是否为TypeAdapter类型
if (TypeAdapter.class.isAssignableFrom(adapterClass)) {
Class<? extends TypeAdapter<?>> typeAdapterClass =
(Class<? extends TypeAdapter<?>>) adapterClass;
return newInstance(typeAdapterClass);
}
// 检查适配器类是否为TypeAdapterFactory类型
if (TypeAdapterFactory.class.isAssignableFrom(adapterClass)) {
Class<? extends TypeAdapterFactory> factoryClass =
(Class<? extends TypeAdapterFactory>) adapterClass;
TypeAdapterFactory factory = newInstance(factoryClass);
return factory.create(GsonBuilder.DEFAULT_GSON, type);
}
// 检查适配器类是否为JsonSerializer类型
if (JsonSerializer.class.isAssignableFrom(adapterClass) ||
JsonDeserializer.class.isAssignableFrom(adapterClass)) {
// 处理JsonSerializer和JsonDeserializer的组合
Type adapterType = getSupertype(adapterClass, JsonAdapter.class, Object.class);
Type[] args = $Gson$Types.getRawType(adapterType).getTypeParameters();
if (args.length != 1) {
throw new IllegalArgumentException(
"@JsonAdapter " + adapterClass.getName() + " must specify one type parameter.");
}
Type adaptsType = $Gson$Types.resolve(adapterType, adapterClass, args[0]);
// 创建适配器包装器
return new TreeTypeAdapter<>(
(JsonSerializer<Object>) newInstance(adapterClass),
(JsonDeserializer<Object>) newInstance(adapterClass),
GsonBuilder.DEFAULT_GSON,
TypeToken.get(adaptsType),
null
);
}
throw new IllegalArgumentException(
"@JsonAdapter value must be TypeAdapter, TypeAdapterFactory"
+ ", JsonSerializer or JsonDeserializer reference.");
}
5.3 @JsonAdapter注解的使用示例
以下是一个使用@JsonAdapter
注解的示例:
java
// 自定义Date类型适配器
@JsonAdapter(DateTypeAdapter.class)
class MyDate {
private Date date;
public MyDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
// 自定义Date类型适配器
class DateTypeAdapter extends TypeAdapter<Date> {
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(dateFormat.format(value));
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
String dateStr = in.nextString();
try {
return dateFormat.parse(dateStr);
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
}
在这个例子中,@JsonAdapter
注解为MyDate
类指定了自定义的DateTypeAdapter
,使得该类在序列化和反序列化时使用特定的日期格式。
六、Gson注解处理的核心组件
6.1 Excluder类
Excluder
类是Gson中处理字段排除的核心组件,它实现了ExclusionStrategy
接口,并根据注解和配置决定哪些字段应该被排除。
java
public final class Excluder implements ExclusionStrategy, Cloneable {
// 各种排除配置
private double version = -1;
private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
private boolean serializeInnerClasses = true;
private boolean requireExpose;
// 序列化和反序列化的排除策略
private final List<ExclusionStrategy> serializationStrategies = new ArrayList<>();
private final List<ExclusionStrategy> deserializationStrategies = new ArrayList<>();
// 检查字段是否应该被排除
@Override
public boolean excludeField(Field field, boolean serialize) {
// 检查字段修饰符
if ((field.getModifiers() & modifiers) != 0) {
return true;
}
// 检查是否为内部类
if (!serializeInnerClasses && isInnerClass(field.getType())) {
return true;
}
// 检查是否为匿名类或本地类
if (isAnonymousOrLocal(field.getType())) {
return true;
}
// 检查版本控制注解
if (version != -1) {
Since since = field.getAnnotation(Since.class);
if (since != null && since.value() > version) {
return true;
}
Until until = field.getAnnotation(Until.class);
if (until != null && until.value() <= version) {
return true;
}
}
// 检查@Expose注解
if (requireExpose) {
Expose expose = field.getAnnotation(Expose.class);
if (expose == null) {
return true;
}
if (serialize ? !expose.serialize() : !expose.deserialize()) {
return true;
}
}
// 应用注册的排除策略
List<ExclusionStrategy> list = serialize
? serializationStrategies
: deserializationStrategies;
for (ExclusionStrategy exclusionStrategy : list) {
if (exclusionStrategy.shouldSkipField(fieldAttributes(field))) {
return true;
}
}
return false;
}
// 其他方法...
}
6.2 ReflectiveTypeAdapterFactory类
ReflectiveTypeAdapterFactory
是Gson中基于反射实现序列化和反序列化的核心工厂类。它负责处理注解并创建相应的适配器。
java
public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
private final ConstructorConstructor constructorConstructor;
private final FieldNamingStrategy fieldNamingPolicy;
private final Excluder excluder;
private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
// 检查是否为接口或抽象类
if (!Object.class.isAssignableFrom(raw)) {
return null;
}
// 处理@JsonAdapter注解
JsonAdapter annotation = type.getRawType().getAnnotation(JsonAdapter.class);
if (annotation != null) {
return jsonAdapterFactory.create(gson, type, annotation);
}
// 创建反射适配器
return new Adapter<>(
constructorConstructor.get(type),
getBoundFields(gson, type, raw)
);
}
// 获取绑定字段的方法
private Map<String, BoundField> getBoundFields(
Gson context, TypeToken<?> type, Class<?> raw) {
// 实现逻辑...
}
// 适配器实现类
static final class Adapter<T> extends TypeAdapter<T> {
private final ObjectConstructor<T> constructor;
private final Map<String, BoundField> boundFields;
// 构造方法和读写方法...
}
// 绑定字段内部类
abstract static class BoundField {
// 字段信息和读写方法...
}
}
6.3 FieldNamingStrategy接口
FieldNamingStrategy
接口定义了Java字段名到JSON字段名的映射策略。Gson提供了多种内置策略,并允许自定义策略。
java
public interface FieldNamingStrategy {
/**
* 将Java字段名转换为JSON字段名
*/
public String translateName(Field f);
}
Gson内置的实现包括:
FieldNamingPolicy.IDENTITY
:使用原始字段名FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES
:将字段名转换为小写并使用下划线分隔FieldNamingPolicy.LOWER_CASE_WITH_DASHES
:将字段名转换为小写并使用连字符分隔FieldNamingPolicy.UPPER_CAMEL_CASE
:将字段名转换为大驼峰格式
七、注解驱动转换的性能考量
7.1 反射开销
基于注解的转换机制依赖Java反射,这会带来一定的性能开销。反射操作(如获取字段、调用方法)比直接代码执行慢得多,尤其是在处理大量对象时。
7.2 优化建议
为了减少注解驱动转换的性能开销,可以考虑以下优化建议:
-
使用自定义TypeAdapter :对于性能敏感的场景,直接实现
TypeAdapter
可以避免反射开销。 -
缓存反射结果:在自定义适配器中缓存反射获取的字段和方法信息,避免重复反射操作。
-
批量处理:尽量批量处理数据,减少对象创建和反射调用的次数。
-
使用ProGuard/R8:通过代码混淆工具移除未使用的字段和方法,减少反射查找范围。
-
避免过度使用注解:只在必要的地方使用注解,避免不必要的注解处理开销。
八、总结与展望
8.1 注解驱动转换的优势
Gson的注解驱动转换机制提供了以下优势:
-
简化开发:通过注解直接声明转换规则,无需编写额外的适配器代码。
-
灵活性:提供多种注解类型,满足不同的转换需求。
-
可维护性:注解直接与Java类关联,使代码更易于理解和维护。
-
扩展性:可以通过自定义注解和适配器进一步扩展Gson的功能。
8.2 未来发展方向
随着Java和Android技术的发展,Gson的注解驱动转换机制可能会在以下方向发展:
-
Kotlin集成:更好地支持Kotlin语言特性,如数据类、协程等。
-
性能优化:减少反射开销,提高注解处理效率。
-
编译时处理:引入编译时注解处理,生成优化的转换代码。
-
增强注解功能:提供更多注解选项,支持更复杂的转换规则。
-
与其他框架集成:更紧密地集成其他Android框架,如Room、Retrofit等。
通过不断优化和扩展,Gson的注解驱动转换机制将继续为Android开发者提供高效、灵活的JSON处理解决方案。