博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 反射
阅读量:5789 次
发布时间:2019-06-18

本文共 5136 字,大约阅读时间需要 17 分钟。

java jdk: 10.0.2

注解的定义

注解通过 @interface 关键字进行定义.

public @interface TestAnnotation {}

它的形式跟接口很类似,不过前面多了一个 @ 符号. 上面的代码就创建了一个名字为 TestAnnotaion 的注解.

你可以简单理解为创建了一张名字为 TestAnnotation 的标签.

注解的应用

上面创建了一个注解, 那么注解的的使用方法是什么呢.

@TestAnnotationpublic class Test {}

创建一个类 Test, 然后在类定义的地方加上 @TestAnnotation 就可以用 TestAnnotation 注解这个类了.

你可以简单理解为将 TestAnnotation 这张标签贴到 Test 这个类上面.

不过, 要想注解能够正常工作, 还需要介绍一下一个新的概念那就是元注解.

元注解

元注解是什么意思呢?

元注解是可以注解到注解上的注解, 或者说元注解是一种基本注解, 但是它能够应用到其它的注解上面.

如果难于理解的话, 你可以这样理解. 元注解也是一张标签, 但是它是一张特殊的标签, 它的作用和目的就是给其他普通的标签进行解释说明的.

元标签有 @Retention@Documented@Target@Inherited@Repeatable 5 种.

@Retention

Retention 的英文意为保留期的意思. 当 @Retention 应用到一个注解上的时候, 它解释说明了这个注解的的存活时间.

它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留, 在编译器进行编译时它将被丢弃忽视.
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候, 它并不会被加载到 JVM 中.
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候, 它会被加载进入到 JVM 中, 所以在程序运行时可以获取到它们.

我们可以这样的方式来加深理解, @Retention 去给一张标签解释的时候, 它指定了这张标签张贴的时间.

@Retention 相当于给一张标签上面盖了一张时间戳, 时间戳指明了标签张贴的时间周期.

@Retention(RetentionPolicy.RUNTIME)public @interface TestAnnotation {}

上面的代码中, 我们指定 TestAnnotation 可以在程序运行周期被获取到, 因此它的生命周期非常的长.

@Documented

顾名思义, 这个元注解肯定是和文档有关. 它的作用是能够将注解中的元素包含到 Javadoc 中去.

@Target

Target 是目标的意思, @Target 指定了注解运用的地方.

你可以这样理解, 当一个注解被 @Target 注解时, 这个注解就被限定了运用的场景.

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性(字段)进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解, 比如类、接口、枚举

@Inherited

Inherited 是继承的意思, 但是它并不是说注解本身可以继承, 而是说如果一个超类被 @Inherited 注解过的注解进行注解的话, 那么如果它的子类没有被任何注解应用的话, 那么这个子类就继承了超类的注解.

说的比较抽象. 代码来解释.

@Inherited@Retention(RetentionPolicy.RUNTIME)@interface Test {}@Testpublic class A {}public class B extends A {}

注解 Test@Inherited 修饰, 之后类 A 被 Test 注解, 类 B 继承 A, 类 B 也拥有 Test 这个注解.

@Repeatable

Repeatable 自然是可重复的意思.

什么样的注解会多次应用呢? 通常是注解的值可以同时取多个.

举个例子, 一个人他既是程序员又是产品经理, 同时他还是个画家.

@interface Persons {    Person[]  value();}@Repeatable(Persons.class)@interface Person{    String role default "";}@Person(role="artist")@Person(role="coder")@Person(role="PM")public class SuperMan{}

注意上面的代码, @Repeatable 注解了 Person. 而 @Repeatable 后面括号中的类相当于一个容器注解.

什么是容器注解呢? 就是用来存放其它注解的地方. 它本身也是一个注解.

我们再看看代码中的相关容器注解.

@interface Persons {    Person[]  value();}

按照规定, 它里面必须要有一个 value 的属性, 属性类型是一个被 @Repeatable 注解过的注解数组, 注意它是数组.

注解的属性

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface TestAnnotation {    int id();    String msg();}

上面代码定义了 TestAnnotation 这个注解中拥有 idmsg 两个属性. 在使用的时候, 我们应该给它们进行赋值.

赋值的方式是在注解的括号内以 value=”” 形式, 多个属性之前用 , 隔开.

@TestAnnotation(id=3,msg="hello annotation")public class Test {}
⚠️ 在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组.

判断类上是否使用指定注解

Map
> stringClassMap = ClassUtil.getClasses("org.itzhizhe.testReflection");stringClassMap.forEach((key, value) ->{ ReflectionClass annotation = value.getAnnotation(ReflectionClass.class); if (annotation != null) { System.out.println("class name: " + value.getSimpleName() + ", 注解 value : " + annotation.Value()); }});

判断字段上是否使用指定注解

Map
> stringClassMap = ClassUtil.getClasses("org.itzhizhe.testReflection"); stringClassMap.forEach((key, value) -> { for (Field declaredField : ClassUtil.getAnnotatedDeclaredFields(value, ReflectionField.class, false)) { ReflectionField annotation = declaredField.getAnnotation(ReflectionField.class); if (annotation != null) { this.put(annotation.Value(), declaredField); System.out.println("class name: " + value.getSimpleName() + ", 字段名: " + declaredField.getName() + ", 字段类型:" + declaredField.getType().getSimpleName() + ", 注解值:" + annotation.Value() ); } } });

判断方法上是否使用指定注解

Map
> stringClassMap = ClassUtil.getClasses("org.itzhizhe.testReflection"); stringClassMap.forEach((key, value) -> { for (Method declaredField : ClassUtil.getAnnotatedDeclaredMethods(value, ReflectionMethod.class,false)) { ReflectionMethod annotation = declaredField.getAnnotation(ReflectionMethod.class); if (annotation != null) { System.out.println("class name: " + value.getSimpleName() + ", 方法名: " + declaredField.getName() + ", 方法返回类型:" + declaredField.getReturnType() + ", 注解值:" + annotation.id() ); } } });

创建对象

value.getConstructor().newInstance();

上面代码是使用无参构造来创建对象, 当然也可以使用有参构造来创建对象.

getConstructor() 参数填写构造参数的参数类型.

newInstance() 参数填写传给构造参数的值.

调用方法

可以通过 MethodsetAccessible 方法设置为 true, 就可以调用对象的 private 方法.

declaredField.setAccessible(true);declaredField.invoke(value);

然后可以使用 invoke 方法来进行方法调用. 第一个参数为要调用方法的对象. 剩下的参数是方法参数.

总结

如果想实现像 Spring 这种框架, 实现上面几步是必须.

比如依赖注入, 我们需要知道字段的类型, 然后到 IOC 容器中找到对应的对象进行赋值.

而方法调用你可以先将注解的方法进行保存, 比如保存到 Map 集合中.

例如客户端发送指定数据帧后, 然后通过 key 取出要执行的方法后执行就可以.

转载地址:http://wgmyx.baihongyu.com/

你可能感兴趣的文章
云南去年有望实现151万贫困人口净脱贫
查看>>
Java架构师面试题系列整理(大全)
查看>>
延伸产业链 中国产粮大省向“精深”问发展
查看>>
消费贷用户70%月收入低于5000元 80、90后是主要人群
查看>>
2018年内蒙古外贸首次突破1000亿元
查看>>
CTOR有助于BCH石墨烯技术更上一层楼
查看>>
被遗忘的CSS
查看>>
Webpack中的sourcemap以及如何在生产和开发环境中合理的设置sourcemap的类型
查看>>
做完小程序项目、老板给我加了6k薪资~
查看>>
java工程师linux命令,这篇文章就够了
查看>>
关于React生命周期的学习
查看>>
webpack雪碧图生成
查看>>
搭建智能合约开发环境Remix IDE及使用
查看>>
Spring Cloud构建微服务架构—服务消费基础
查看>>
RAC实践采坑指北
查看>>
runtime运行时 isa指针 SEL方法选择器 IMP函数指针 Method方法 runtime消息机制 runtime的使用...
查看>>
LeetCode36.有效的数独 JavaScript
查看>>
Scrapy基本用法
查看>>
PAT A1030 动态规划
查看>>
自制一个 elasticsearch-spring-boot-starter
查看>>