注解是 Java 5 引入的一个新特性,它提供了一个用来将信息和元数据与程序元素相关联的能力,其作用如同一个修饰符,本身并不包含任何程序逻辑。
本文将介绍如何创建和使用自定义的注解。
元注解
Java 自带了四个作用于注解上的注解,即元注解,分别是:
@Documented
,用于注明该注解是否包含于 JavaDoc 中
@Retention
,用于注明这个注解将保留到什么时候
@Target
,用于注明这个注解将作用于哪些元素上
@Inherit
,用于注明该注解是否会被子类继承
@Retention
@Retention
元注解定义了这个注解的生命周期,即这个注解将保留到什么时候。注解的生命周期有这三种:
RetentionPolicy.SOURCE
:仅在源码中保留,在编译期就会被丢弃。比如 @Override
和 @SuppressWarnings
就属于这类注解
RetentionPolicy.CLASS
:注解将会被写入到字节码中,但是在运行时会被丢弃。这个是默认的生命周期。
RetentionPolicy.RUNTIME
:该注解将保留至运行时。这意味着在运行时可以通过反射机制读取到注解的信息。
@Target
@Target
元注解指定了该注解将可用于哪些元素上。可用的参数有如下几种:
ElementType.ANNOTATION_TYPE
,用于描述注解。@Target(ElementType.ANNOTATION_TYPE)
标注的注解将成为一个元注解。
ElementType.CONSTRUCTOR
,用于描述构造方法
ElementType.FIELD
,用于描述成员变量、对象、属性(包括 enum 实例)
ElementType.LOCAL_VARIABLE
,用于描述局部变量
ElementType.METHOD
,用于描述方法
ElementType.PACKAGE
,用于描述包
ElementType.PARAMETER
,用于描述参数
ElementType.TYPE
,用于描述类、接口(包括注解)、enum 生命声明
Java 8 中又新增了两个参数:
ElementType.TYPE_PARAMETER
,可以用在 Type 的声明前
ElementType.TYPE_USE
,可以用在使用 Type 的地方
编写自定义注解及相关方法
自定义注解的类型为 @interface
,注解中可以包含方法,方法名将作为注解的属性。
注解中的方法不可以有参数,也不可以抛出异常,同时方法只能返回原始类型、String
、Class
、enums
、注解类型,以及上述类型的数组。方法的默认值不可以是 null
。
下面将通过一个示例演示如何编写和使用自定义注解相关的方法。
示例将分别创建两个名为 @JsonSerializable
和 @JsonElement
的注解,以及一个名为 JsonUtils
的工具类。
@JsonSerializable
标记一个类可以被序列化成 JSON,@JsonElement
标记一个成员变量将会被包含在这个 JSON 中;JsonUtils
工具类包含将对象序列化为 JSON 的方法。
@JsonSerializable
1 2 3 4 5 6 7 8 9 10 11
|
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface JsonSerializable { }
|
@JsonElement
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface JsonElement {
public String key() default ""; }
|
JsonUtils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| public class JsonUtils {
public static String serializeToJson(Object object) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
if (!isSerializable(object)) { return null; }
Class clazz = object.getClass();
Method[] methods = clazz.getMethods();
List<String> methodNames = Arrays.stream(methods).map(Method::getName).collect(Collectors.toList());
Field[] fields = clazz.getDeclaredFields();
Map<String, String> elements = new HashMap<>(fields.length);
for (Field field : fields) { if (field.isAnnotationPresent(JsonElement.class)) {
String fieldName = field.getName();
String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
if (methodNames.contains(getterName)) {
Method method = clazz.getMethod(getterName);
String keyName = field.getAnnotation(JsonElement.class).key();
elements.put("".equals(keyName) ? field.getName() : keyName, String.valueOf(method.invoke(object))); } } }
String jsonBody = elements.entrySet() .stream() .map(entry -> "\t\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"") .collect(Collectors.joining(",\n"));
return "{\n" + jsonBody + "\n}"; }
private static boolean isSerializable(Object object) {
if (object == null) { return false; }
Class clazz = object.getClass();
return clazz.isAnnotationPresent(JsonSerializable.class); } }
|
使用自定义注解
首先创建一个 BookModel
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@JsonSerializable public class BookModel {
@JsonElement(key = "bookname") private String bookName;
@JsonElement private String category;
@JsonElement private int price;
@Override public String toString() { return "BookModel{" + "bookName='" + bookName + '\'' + ", category='" + category + '\'' + ", price=" + price + '}'; }
}
|
接下来在 main 方法里构造对象,并将其序列化成 JSON:
1 2 3 4 5 6 7 8 9 10
| public static void main(String[] args) {
BookModel book = new BookModel("Head First Java", "Java", 55);
try { System.out.println(JsonUtils.serializeToJson(book)); } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { e.printStackTrace(); } }
|
序列化后的结果将是这样:
1 2 3 4 5
| { "bookname":"Head First Java", "category":"Java", "price":"55" }
|