1.定义注解
JDK5开始引入注解,Java注解是一种在源码中添加元信息的机制。本文将创建一个自定义注解用来将对象转换为JSON字符串。
通过@interface关键字定义注解。
public @interface JsonSerializable {
}
接着通过Retention和Target定义元信息。 这个注解仅在运行时可见,并作用在类上。通过定义这个注解标记一个类,表示类可以被序列化为JSON。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Type)
public @interface JsonSerializable {
}
接着创建第二个注解来标识类变量。这里通过方法定义了注解,表明有一个字符串参数key,默认值为空。当通过方法创建自定义注解,方法不能含有参数,不能抛出异常。返回值必须为原始类型,String,Class,enums,annotations及上述类型的构成的数组类型,默认值不能为null。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonElement {
public String key() default "";
}
接着定义标记方法的注解,被标记的方法用来完成对象的初始化。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Init {
}
2.使用注解
下面定义了一个Person类,表示我们希望序列化的类。
@JsonSerializable
public class Person {
@JsonElement
private String firstName;
@JsonElement
private String lastName;
@JsonElement(key = "personAge")
private String age;
private String address;
@Init
private void initNames() {
this.firstName = this.firstName.substring(0, 1).toUpperCase()
+ this.firstName.substring(1);
this.lastName = this.lastName.substring(0, 1).toUpperCase()
+ this.lastName.substring(1);
}
// Standard getters and setters
}
@JsonSerializable表明Person能被序列化为JSON字符串。被@JsonElement注解标记的字段才会被输出。@Init标记的方法将在序列化前执行。
3.注解处理器
接下来通过反射API来获取这些注解信息。
首先检查类是否被@JsonSerializable注解标记。
private void checkIfSerializable(Object object) {
if (Objects.isNull(object)) {
throw new JsonSerializationException("The object to serialize is null");
}
Class<?> clazz = object.getClass();
if (!clazz.isAnnotationPresent(JsonSerializable.class)) {
throw new JsonSerializationException("The class "
+ clazz.getSimpleName()
+ " is not annotated with JsonSerializable");
}
}
接着寻找被 @Init 注解标记的方法,并执行该方法来完成对象字段初始化。
private void initializeObject(Object object) throws Exception {
Class<?> clazz = object.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Init.class)) {
method.setAccessible(true);
method.invoke(object);
}
}
}
接着检查被@JsonElement标记的字段,随后获取JSON元素的key与value,最后创建JSON字符串。
private String getJsonString(Object object) throws Exception {
Class<?> clazz = object.getClass();
Map<String, String> jsonElementsMap = new HashMap<>();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(JsonElement.class)) {
jsonElementsMap.put(getKey(field), (String) field.get(object));
}
}
String jsonString = jsonElementsMap.entrySet()
.stream()
.map(entry -> "\"" + entry.getKey() + "\":\""
+ entry.getValue() + "\"")
.collect(Collectors.joining(","));
return "{" + jsonString + "}";
}
private String getKey(Field field) {
String value = field.getAnnotation(JsonElement.class)
.key();
return value.isEmpty() ? field.getName() : value;
}
最后创建转换对象。
public class ObjectToJsonConverter {
public String convertToJson(Object object) throws JsonSerializationException {
try {
checkIfSerializable(object);
initializeObject(object);
return getJsonString(object);
} catch (Exception e) {
throw new JsonSerializationException(e.getMessage());
}
}
}
验证结果
@Test
public void givenObjectSerializedThenTrueReturned() throws JsonSerializationException {
Person person = new Person("soufiane", "cheouati", "34");
ObjectToJsonConverter serializer = new ObjectToJsonConverter();
String jsonString = serializer.convertToJson(person);
assertEquals(
"{\"personAge\":\"34\",\"firstName\":\"Soufiane\",\"lastName\":\"Cheouati\"}",
jsonString);
}
4.总结
自定义注解的使用最终可以归纳为:
- 定义注解
- 在目标上使用注解
- 定义注解处理器
- 通过注解处理器使用被注解标注的目标
通过定义注解并使用可以简化代码,提供开发效率。