什么是注解?
Retention和Target
策略 |
描述 |
Source |
编译器会丢弃注解 |
Class |
注解是在编译器生成的类文件中记录的,但不需要在运行时处理类文件的Java虚拟机(JVM)保留。 |
Runtime |
注解由编译器记录在类文件中,并由JVM在运行时保留 |
目标 |
描述 |
Annotation Type |
注解另一个注解 |
Constructor |
注解构造函数 |
Field |
注解一个字段,例如类的实例变量或枚举常量 |
Local variable |
注解局部变量 |
Method |
注解类的方法 |
Module |
注解模块(Java 9中的新增功能) |
Package |
注解包 |
Parameter |
注解到方法或构造函数的参数 |
Type |
注解一个类型,例如类,接口,注解类型或枚举声明 |
Type Parameter |
注解类型参数,例如用作通用参数形式的参数 |
Type Use |
注解类型的使用,例如当使用new关键字创建类型的对象时 ,当对象强制转换为指定类型时,类实现接口时,或者使用throws关键字声明throwable对象的类型时(有关更多信息,请参阅Type Annotations and Pluggable Type Systems Oracle tutorial) |
注解参数
如何创建注解?
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonField {
public String value() default "";
}复制代码
我们声明的核心是public @interface JsonField,声明带有public修饰符的注解——允许我们的注解在任何包中使用(假设在另一个模块中正确导入包)。注解声明一个String类型value的参数,默认值为空字符串。
如何使用注解?
public class Car {
@JsonField("manufacturer")
private final String make;
@JsonField
private final String model;
private final String year;
public Car(String make, String model, String year) {
this.make = make;
this.model = model;
this.year = year;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public String getYear() {
return year;
}
@Override
public String toString() {
return year + " " + make + " " + model;
}
} 复制代码
该类使用@JsonField注解的两个主要用途:(1)具有显式值,(2)具有默认值。我们也可以使用@JsonField(value = "someName")注解一个字段,但这种样式过于冗长,并没有助于代码的可读性。因此,除非在单元素注解中包含注解参数名称可以增加代码的可读性,否则应该省略它。对于具有多个参数的注解,需要显式指定每个参数的名称来区分参数(除非仅提供一个参数,在这种情况下,如果未显式提供名称,则参数将映射到value参数)。
如何处理注解?
public class JsonSerializer {
public String serialize(Object object) throws JsonSerializeException {
try {
Class<?> objectClass = requireNonNull(object).getClass();
Map<String, String> jsonElements = new HashMap<>();
for (Field field : objectClass.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(JsonField.class)) {
jsonElements.put(getSerializedKey(field), (String) field.get(object));
}
}
System.out.println(toJsonString(jsonElements));
return toJsonString(jsonElements);
} catch (IllegalAccessException e) {
throw new JsonSerializeException(e.getMessage());
}
}
private String toJsonString(Map<String, String> jsonMap) {
String elementsString = jsonMap.entrySet().stream().map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"").collect(Collectors.joining(","));
return "{" + elementsString + "}";
}
private static String getSerializedKey(Field field) {
String annotationValue = field.getAnnotation(JsonField.class).value();
if (annotationValue.isEmpty()) {
return field.getName();
} else {
return annotationValue;
}
}
} 复制代码
请注意,为简洁起见,已将多个功能合并到该类中。有关此序列化程序类的重构版本,请参阅codebase存储库中的此分支(https://github.com/albanoj2/dzone-json-serializer/tree/srp_generalization)。我们还创建了一个异常,用于表示在serialize方法处理对象时是否发生了错误:
public class JsonSerializeException extends Exception {
private static final long serialVersionUID = -8845242379503538623L;
public JsonSerializeException(String message) {
super(message);
}
} 复制代码
尽管JsonSerializer该类看起来很复杂,但它包含三个主要任务:(1)查找使用@JsonField注解的所有字段,(2)记录包含@JsonField注解的所有字段的名称(或显式提供的字段名称)和值,以及(3)将所记录的字段名称和值的键值对转换成JSON字符串。
Car car=new Car("Ford","F150","2018");
JsonSerializer serializer=new JsonSerializer();
serializer.serialize(car); 复制代码
输出:
{"model":"F150","manufacturer":"Ford"}复制代码