目录
- 背景
- 过程
- 注解概念
- 明确自定义注解的边界
- 1、定义注解:使用 @interface 关键字定义一个新的注解。注解可以包含元素,用于指定注解的属性。
- 2、使用注解:在适当的地方使用自定义注解。可以将注解应用于类、方法、字段等。
- 3、解析注解:在程序中通过反射机制解析和处理注解。可以获取注解的属性值,并根据注解的定义执行相应的操作。
- 手写反射实现自定义注解
- 结果
背景
凡事我们都要知其然,知其所以然,我们既然要研究反射,就要知道
反射的原理是什么:在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 {@Overridepublic 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);}@Overridepublic 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);}@Overridepublic String errorMsg() {
// String errorMessage = this.readFile("errorMsg");
// System.out.println("错误信息:"+ errorMessage);
// return errorMessage;String errorMsgValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());return errorMsgValue;}@Overridepublic 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.classString 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应用程序的开发。
测试和调试:注解可以用于标记测试用例或调试相关的信息,以便测试框架或调试器能够自动化执行相应的操作。例如,通过注解标记测试方法,测试框架可以识别并执行这些方法作为测试套件的一部分。