Java注解原理分析,自定义注解并生效实例
程序员文章站
2024-01-11 16:02:46
目录撸个注解注解是什么注解的生命周期注解修饰的目标开始撸注解使用注解测试注解最后撸个注解注解是java中十分重要的一部分,我们无时无刻都在使用这他,尤其是使用Spring框架开发的时候,但是如何自定义一个注解呢,或者注解到底是如何生效的呢,工作原理是什么呢?接下来我们就一起动手撸一个注解注解是什么注解是Java1.5时引入的概念,属于一种类型。注解提供了一系列数据用来修饰程序代码(类、方法、字段),但是注解并不是所修饰代码的一部分,即它对代码的运行没有直接影响,由编译器决定该执行那些操作注...
撸个注解
注解是java中十分重要的一部分,我们无时无刻都在使用这他,尤其是使用Spring框架开发的时候,但是如何自定义一个注解呢,或者注解到底是如何生效的呢,工作原理是什么呢?接下来我们就一起动手撸一个注解
注解是什么
注解是Java1.5时引入的概念,属于一种类型。注解提供了一系列数据用来修饰程序代码(类、方法、字段),但是注解并不是所修饰代码的一部分,即它对代码的运行没有直接影响,由编译器决定该执行那些操作
注解的生命周期
注解的声明周期有三种策略,定义在RetentionPolicy枚举中
- SOURCE:在源文件中有效,被编译器丢弃
- CLASS:在编译器生成的字节码文件中有效,但在运行时会被处理类文件的JVM丢弃
- RUNTIME:在运行时有效。这也是注解声明周期中最常用的一种策略,它允许持续通过反射的方式访问注解,并根据注解的定义执行相应的代码
注解修饰的目标
注解的目标定义了注解将适用于哪一种级别的Java代码上,有些注解只适用于方法,
在ElementType中定义了11中注解的类型
- TYPR:用于类、接口、注解、枚举
- FIELD:用于字段(类的成员变量),或者枚举常量
- METHOD:用于方法
- PARAMETER:用于普通方法或者构造方法的参数
- CONSTRUCTOR:用于构造方法
- LOCAL_VARIABLE:用于变量
- ANNOTATION_TYPE:用于注解
- PACKAGE:用于包
- TYPE_PARAMETER:用于泛型参数
- TYPE_USE:用于声明语句、泛型或者强制转换语句中的类型
- MODULE:用于模块
开始撸注解
来撸一个字段的注解,用来标记对象在序列化成JSON的时候要不要包含这个字段
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface JsonField { public String value() default ""; }
-
解释:
- JsonField注解的生命周期是RUNTIME,即运行时有效
- JsonField注解修饰的目标是FIELD,也就是针对字段的
- 创建注释需要用到@interface关键字
- JsonField注解只有一个参数,名字为value,类型是String,默认值为一个空字符串
参数名为value,允许注解的使用者提供一个无需指定名字的参数。即我们可以在一个字段上使用@JsonField(value=“冢狐”),也可以把value=省略,变成@JsonField(“冢狐”)
- default “ ”允许我们在一个字段上直接使用@JsonField,而无需指定参数的名和值
使用注解
创建一个类,包含三个字段:age、name、address,后两个是必选序列化的字段
public class People { private int age ; @JsonField("writeName") private String name; @JsonField private String address; public People(int age,String name,String address){ this.age=age; this.name=name; this.address=address; } @Override public String toString() { return "People{" + "age=" + age + ", name='" + name + '\'' + ", address='" + address + '\'' + '}'; } }
其中:
- name上的@JsonField注解提供了显示的字符串值
- address上的@JsonField注解使用了缺省值
接下来我们编写序列化类JsonSerializer:
public class JsonSerializer { public static String serialize(Object object) throws IllegalAccessException { Class<?> objectClass = 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)); } } return toJsonString(jsonElements); } private static String getSerializedKey(Field field) { String annotationValue = field.getAnnotation(JsonField.class).value(); if (annotationValue.isEmpty()) { return field.getName(); } else { return annotationValue; } } private static String toJsonString(Map<String, String> jsonMap) { String elementsString = jsonMap.entrySet() .stream() .map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"") .collect(Collectors.joining(",")); return "{" + elementsString + "}"; } }
下面我们看一下各自的含义以及作用:
-
serialize()
方法是用来序列化对象的,它接收一个 Object 类型的参数。objectClass.getDeclaredFields()
通过反射的方式获取对象声明的所有字段,然后进行 for 循环遍历。在 for 循环中,先通过field.setAccessible(true)
将反射对象的可访问性设置为 true,供序列化使用(如果没有这个步骤的话,private 字段是无法获取的,会抛出 IllegalAccessException 异常);再通过isAnnotationPresent()
判断字段是否装饰了JsonField
注解,如果是的话,调用getSerializedKey()
方法,以及获取该对象上由此字段表示的值,并放入 jsonElements 中。 -
getSerializedKey()
方法用来获取字段上注解的值,如果注解的值是空的,则返回字段名。 -
toJsonString()
方法借助 Stream 流的方式返回格式化后的 JSON 字符串。
测试注解
public class JsonFileTest { public static void main(String[] args) throws IllegalAccessException{ People cmower = new People(18,"冢狐","中国"); System.out.println(JsonSerializer.serialize(cmower)); } }
- 结果:
{“writeName”:“冢狐”,“address”:“中国”}
-
分析
- 首先age字段没有被@JsonField注解所以没有序列化
- name修饰了@JsonField注解,并且显示指定了字符串writerName,所以序列化后变成了writeName
- address字段修饰了@JsonField注解,但是没有显示指定值,所以序列化后还是address
最后
- 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
- 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
- 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。
本文地址:https://blog.csdn.net/issunmingzhi/article/details/108262149