写点什么

java 注解原理

  • 2023-11-24
    浙江
  • 本文字数:2489 字

    阅读完需:约 8 分钟

java 注解原理

最近在看公司的一个数据推送的源码,发现很多地方用到了自定义注解,就很好奇注解到底能做什么,仅仅定义了一个 @interface 就把很多事情都做完了。故接下来从四个方向去认识注解

注解是什么

Java 注解(Annotation)是 Java 语言提供的一种元数据(metadata)机制,它允许在代码中添加额外的信息和说明。注解以@符号开头,紧跟注解的名称和可选的参数。常见的元数据注解有:

@Target:用于指定注解的适用目标,如类、方法、字段等

@Override:用于标记一个方法覆盖(重写)了父类中的方法。

@Deprecated:用于标记一个方法、类或字段已过时,不推荐使用。

@FunctionalInterface:用于标记一个接口是函数式接口,即只包含一个抽象方法的接口。

@Retention:用于指定注解的保留策略,即注解在编译时、类加载时还是在运行时可见

@Documented:用于指定注解将包含在 Java 文档中

@Inherited:用于指定注解是否可被继承。


注解能做什么

  1. 文档:可以利用 @Deprecated 注解生成接口的 yapi 等相关文档

  2. 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】

  3. 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override 等】 比如说 @Test

注解的使用

要使用注解之前,要明确想用这个注解做什么,然后定义其属性,以及在什么阶段进行处理

比如说我现在要定义一个入参校验器注解

@Target(ElementType.FIELD) //作用在字段上@Retention(RetentionPolicy.RUNTIME) //在程序运行期间public @interface FieldValidation {    int maxLength() default Integer.MAX_VALUE; //字段最大长度    String regex() default ""; //正则匹配    boolean required() default false; //是否需要当前参数}
复制代码

当前注解要作用在哪个类上面


public class Person {    @FieldValidation(maxLength = 20, required = true, regex = "[A-Za-z]+")    private String name;
@FieldValidation(maxLength = 20) private String address;

public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }}
复制代码

校验器:


public class FieldValidator {  	public static void validate(Object obj) throws IllegalArgumentException, IllegalAccessException {      //获取当前对象的class实例      Class<?> clazz = obj.getClass();      //获取定义的字段      Field[] fields= clazz.getDeclaredFields();      for(Field field:fields){          //字段上的注解是否为当下定义的注解          if(field.isAnnotationPresent(FieldValidation.class)){              //获取注解对象              FieldValidation annotation = field.getDeclaredAnnotation(FieldValidation.class);              field.setAccessible(true);              Object value = field.get(obj);              if(annotation.required() && value == null){                  throw new IllegalArgumentException("field" +field.getName() +"is required");
} if(value instanceof String && ((String) value).length()>annotation.maxLength()){ throw new IllegalArgumentException("field "+field.getName()+" exceeds maxNum length"); }
if(annotation.regex().length()>0 && value instanceof String){ String regex = annotation.regex(); if(!((String)value).matches(regex)){ throw new IllegalArgumentException("field"+field.getName() +"does not match regex pattern"); } }
if(value instanceof Integer && ((Integer)value >10 || (Integer)value<0)){ throw new IllegalArgumentException("field" +field.getName() +"integer exceeds maxNum length"); } } }

}
复制代码

接下来写一个 test case 进行测试

@Test    void testFieldValidation() {        Person person = new Person();        person.setName("John");        person.setAddress("123 Main Street");        person.setIdNumber(8);        FieldValidator fieldValidator = new FieldValidator();        try {            fieldValidator.validate(person);            System.out.println("Validation passed");        } catch (IllegalAccessException e) {            System.out.println("Validation field"+ e.getMessage());        }
}
复制代码


注解的底层

在上面校验器代码中,有一个点

当前获取的 annotation 就是 @Interface 定义的 FieldValidation 吗

我们知道是,所有的注解的父类都是 annotaion 这个接口 , 其实 FieldValidation 也没有实现,只是定义了几个元素,按照 java 的规范,接口一定要实现的,那么现在要去看下这个实现的。

debug ---------------

proxy 好家伙,这不是动态代理的节奏吗

那么跟进去 field.getDeclaredAnnotation 进去看看


其内部实现要看 declaredAnnotations()


declaredAnnotations 其实是一个 map 如下:

Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

很明显,一开始这个 map 是空 并且需要进行解析注解

故问题就交给 AnnotationParser.parseAnnotations 来处理了

AnnotationParser.parseAnnotations 有三参数

  1. annotation: 字节码数组,java 内部一般先将.java 转为.class 的字节码文件

  2. ConstantPool:常量池

又将其委托给内部的 parseAnnotations2 处理

做了一堆事情后,最后是要走到 annotationMap 将数据存储下来

这个方法里面是走了一个代理工厂的方式创建数据了,最后将类实现保存到 annotations 这个 map 中


故我们的代码执行,实际上一定是代理类在执行


发布于: 刚刚阅读数: 4
用户头像

年少不愿写的代码, 现在一行都躲不掉 2018-12-20 加入

嘿嘿嘿,搬砖ing

评论

发布
暂无评论
java 注解原理_注解_橙子橘子柚子皮_InfoQ写作社区