手写反射实现读取自定义注解中的属性值

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 手写反射实现读取自定义注解中的属性值

背景

凡事我们都要知其然,知其所以然,我们既然要研究反射,就要知道

反射的原理是什么:在Java中,反射(Reflection)是指在运行时获取和操作类的信息的能力。它允许程序在运行时动态地检查类、获取类的属性和方法,并且可以调用类的方法或创建类的实例。

但是他到底是怎么做到运行时获取到信息的呢?我们一起来看一看吧,归根结底,还是匹配字符串,但是是如何匹配的我们还需要考究一下,下面我就手写自定义注解的例子讲解如何使用反射的,并把他手写出来。

过程

注解概念

当应用5W2H方法分析注解时,我们可以按照以下方式来描述注解的作用和含义:

What(什么):注解是一种特殊的标记,用于提供元数据。它可以应用于类、方法、字段等程序元素上,以附加额外的信息。

Why(为什么):注解的目的是为了丰富和增强代码的表达能力和功能。它可以提供额外的元数据,用于编译时检查、运行时处理和自动生成代码等。

Where(在哪里):注解可以应用于各种程序元素,包括类、方法、字段等。具体可以根据注解的定义和目的来决定它可以应用的位置。

When(何时):注解在编码过程中可以随时使用。根据需求,可以在需要注解的代码上使用它们,例如标记过时的方法、指定配置信息等。

Who(谁):注解可以由开发人员或框架进行定义和使用。开发人员可以为自己的代码添加注解,而框架可以使用注解来实现特定的行为或功能。

How(如何):注解通过使用@符号作为前缀,并提供注解名称和可选的参数来声明和使用。开发人员可以自定义注解,并通过反射机制来解析注解并采取相应的操作。

How much(多少):注解的数量和复杂性取决于具体的应用场景和需求。Java提供了一些内置的注解,同时也可以根据需要自定义注解。

明确自定义注解的边界

在Java中,注解(Annotation)是一种用于提供元数据的特殊标记。它可以应用于类、方法、字段等程序元素上,以提供关于这些元素的额外信息。注解本身不会直接影响程序的执行,但可以被编译器、工具和框架等处理,以实现特定的行为或功能。

1、定义注解:使用 @interface 关键字定义一个新的注解。注解可以包含元素,用于指定注解的属性。

// 定义一个自定义注解
public @interface MyAnnotation {
    String value();
    int count() default 0;
}

2、使用注解:在适当的地方使用自定义注解。可以将注解应用于类、方法、字段等。

// 使用自定义注解
@MyAnnotation(value = "Hello", count = 5)
public class MyClass {
    @MyAnnotation("World")
    private String message;
    @MyAnnotation(value = "Method")
    public void myMethod() {
        // 方法体
    }
}

3、解析注解:在程序中通过反射机制解析和处理注解。可以获取注解的属性值,并根据注解的定义执行相应的操作。

// 解析注解
Class<?> clazz = MyClass.class;
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
String value = annotation.value();
int count = annotation.count();

手写反射实现自定义注解

我们主要是实现自定义注解步骤的第三步,上述的步骤是使用getAnnotation

方法获取的注解的相关信息,我们现在不使用这个API,自己手写一个实现文本匹配从而实现getAnnotation的效果:

public class Client {
    public static void main(String[] args) throws IOException, NoSuchFieldException, ClassNotFoundException {
        MapImpl map = new MapImpl();
        map.readFileInfo();
        System.out.println(map.min());
        System.out.println(map.max());
        System.out.println(map.errorMsg());
    }
}
@Target({ElementType.FIELD})
//表示自定义注解可保留到运行时
@Retention(RetentionPolicy.RUNTIME)
//自定义注解,注解名字是Length
public @interface Length {
    // 允许的字符串长度最小值
    int min();
    // 的允许字符串长度最大值
    int max();
    // 自定义错误提示
    String errorMsg();
}

在这个类里边写识别字符串的操作。

public class MapImpl implements Length {
    @Override
    public int min()  {
//        String min = this.readFile("min");
//        System.out.println("最小值为:"+min);
//        return Integer.valueOf(min) ;
        String minValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return Integer.valueOf(minValue);
    }
    @Override
    public int max() {
//        String max = this.readFile("max");
//        System.out.println("最大值为:"+max);
//        return Integer.valueOf(max) ;
        String maxValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return Integer.valueOf(maxValue);
    }
    @Override
    public String errorMsg() {
//        String errorMessage = this.readFile("errorMsg");
//        System.out.println("错误信息:"+  errorMessage);
//        return errorMessage;
        String errorMsgValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return errorMsgValue;
    }
    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }
    boolean state=true;
    private String getMethedValue(String name){
        if (state==true){
            state=false;
            methedValue= this.readFileInfo();
        }
        if(name.equals("errorMsg")){
            System.out.println("错误信息:"+  methedValue.get(name));
            return methedValue.get(name);
        }else if(name.equals("min")){
            return methedValue.get(name);
        }else if(name.equals("max")){
            System.out.println("最大值为:"+methedValue.get(name));
            return methedValue.get(name);
        }
        return null;
    }
    Map<String,String> methedValue = new HashMap<>();
    public Map<String, String> readFileInfo()  {
        try{
            BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入要读取文件的路径");
            //E:\zy\TGB-zgy-2022\老师设计模式课相关资料必须留着\米老师设计模式课小例子\JAVAtest\customAnnotations\target\classes\LengthImpl1\Person.class
            String fileName=br.readLine();
            DataInputStream csanner = new DataInputStream(new FileInputStream(new File(fileName)));
            ClassFile classFile = new ClassFile(csanner);
            List<FieldInfo> fieldsList = classFile.getFields();
            for (FieldInfo field:fieldsList){
                for (AttributeInfo attributeInfo : field.getAttributes()) {
                    System.out.println(attributeInfo);
                    String annotationValue = attributeInfo.toString().replace("(", ",");
                    String annotationValue1 = annotationValue.replace(")", ",");
                    String[] fields = annotationValue1.split(",");
                    for (int i = 1; i < fields.length; i++) {
                        String[] fieldSplit = fields[i].split("=");
                        methedValue.put(fieldSplit[0].trim(),fieldSplit[1].trim());
                        System.out.println(methedValue);
                    }
                }
            }
        }catch (Exception e){
            System.out.println("获取文件失败"+e);
        }
        return methedValue;
    }
}

实现效果:

了解元注解(注解使用的地方,时间范围,是否生成文档,是否被继承)

@Retention:

指定注解的保留策略。它决定注解在编译后是否被保留在class文件中以及在运行时可见性。常用的保留策略包括:

RetentionPolicy.SOURCE:注解仅在源代码级别保留,不包含在编译后的class文件中。

RetentionPolicy.CLASS:注解在编译时被保留,并包含在class文件中,但在运行时不可见。

RetentionPolicy.RUNTIME:注解在编译时被保留,并包含在class文件中,可以在运行时通过反射机制获取到。

@Target:

指定注解可以应用的目标元素类型。它规定了注解可以用于哪些程序元素,如类、方法、字段等。常用的目标元素类型包括:

ElementType.TYPE:注解应用于类、接口、枚举等类型。

ElementType.METHOD:注解应用于方法。

ElementType.FIELD:注解应用于字段(属性)。

ElementType.PARAMETER:注解应用于方法的参数。

@Documented:

指定注解是否包含在生成的API文档中。如果一个注解被标记为@Documented,那么当使用javadoc工具生成API文档时,注解会包含在文档中。

@Inherited:

指定注解是否可以被继承。如果一个注解被标记为@Inherited,那么它可以被继承,即当一个类继承自有该注解的父类时,子类也会继承该注解。

结果

注解的好处:

代码文档化:注解可以用于生成代码文档,通过添加注解来标记类、方法或字段的说明,使得开发者和其他人员能够更好地理解代码的意图和用法。

编译时检查:注解可以用于在编译时对代码进行检查,以确保代码的正确性或进行静态分析。例如,通过自定义注解来标记特定的代码结构,编译器可以根据注解执行额外的检查或生成警告信息。

运行时处理:注解可以在运行时被解析和处理,用于执行特定的逻辑或配置应用程序。例如,通过注解可以配置依赖注入框架,实现对象的自动注入;或者使用注解来定义路由映射,简化Web应用程序的开发。

测试和调试:注解可以用于标记测试用例或调试相关的信息,以便测试框架或调试器能够自动化执行相应的操作。例如,通过注解标记测试方法,测试框架可以识别并执行这些方法作为测试套件的一部分。


相关文章
|
存储 SQL Java
Java反射读取注解信息
Java反射读取注解信息
74 0
|
安全 Java 编译器
Java注解(一) 注解的属性,格式,说明
Java注解(一) 注解的属性,格式,说明
126 0
|
Java
java注解(作用于注解)
java注解(作用于注解)
124 0
|
Java 关系型数据库 MySQL
如何使用@Value注解为bean的属性赋值呢
如何使用@Value注解为bean的属性赋值呢
|
Java 程序员 API
枚举,注解 ,反射
枚举,注解 ,反射
88 0
|
Java 数据库连接 编译器
Java注解详解,自定义注解,利用反射解析注解
概要 这篇文章将会带领你了解Java注解,注解的使用,注解的解析,利用反射解析运行时注解,相信有一定Java基础的小伙伴一定会接触大量的注解,Spring , Hibernate , MyBatis等著名的框架也有很多关于注解方面的应用,对于注解的使用...
2648 0
|
JavaScript Java 程序员
注解与反射笔记
什么是注解 什么是内置注解 元注解 自定义注解 反射 动态语言 V S 静态语言 Java Reflection Java反射机制提供的功能 Java反射优点和缺点 反射相关的主要API Class类
184 0
注解与反射笔记
|
安全 Java
注解和反射12.动态创建对象执行方法
注解和反射12.动态创建对象执行方法