> 文章列表 > 手写Spring框架-前奏-反射获取Annotation

手写Spring框架-前奏-反射获取Annotation

手写Spring框架-前奏-反射获取Annotation

目录

所谓反射

反射机制的作用

反射依赖reflect和Class

反射依赖的Class

Class类的特点

获取Class对象的三种方式

获取类的构造方法并使用

获取类的成员变量并使用

获取类的成员方法并使用

问题引入

解析类的注解

解析成员变量的注解标签

解析方法上的注解

注解获取属性值的底层实现


  • 所谓反射

  • 允许一个java类获得它所有的成员变量和方法,并且显示出来
  • 允许程序在运行时来进行自我检查并且对内部的成员进行操作
  • 反射主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义
  • 只要给定java类的package和名字,就可以通过反射获得类的所有信息
  • 反射机制的作用

  • 在运行时判断任意一个对象所属的类
  • 在运行时获取类的对象
  • 在运行时访问java对象的属性, 方法和构造方法等
  • 反射依赖reflect和Class

  • java.lang.reflect类库里面主要的类
    • Field :表示类中的成员变量
    • Method :表示类中的方法
    • Constructor :表示类的构造方法
    • Array :该类提供了动态创建数组和访问数组元素的静态方法
  • 反射依赖的Class

  • 用来表示运行时类型信息的对应类
  • 每个类都有唯一一个与之相对应的Class对象
  • Class类为类类型,而Class对象为类类型对象
  • Class类的特点

  • Class类也是类的一种, class则是关键字
  • Class类只有一个私有的构造函数(无法通过new来获取实例),只有JVM能够创建Class类的实例
  • JVM中只有唯一一个和类相对应的Class对象来描述其类型信息
  • 获取Class对象的三种方式

  • Object 一> getClass()
  • 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  • 通过Class类的静态方法 : forName(String className)(常用)
  • 在运行期间,一个类,只有一个与之相对应的Class对象产生

  • 通过反射读取配置文件,可以得到该类的相关一切信息
  • Class对象就像一面镜子,透过这面镜子可以看到类的结构
  • 获取类的构造方法并使用

  • 带有Declared都是无视修饰符的
  • 通过Class对象可以获取某个类中的:构造方法
  • 获取构造方法:
  • (1)-批量的方法:
    • public Constructor[] getConstructors():所有"公有的"构造方法
    • public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
  • (2)-获取单个的方法
    • public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法
    • public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有
  • (3)-调用构造方法
    • Constructor.newInstance(Object... initargs)
  • 可以通过反射访问私有的构造方法,不过要设置权限setAccessible(true)
  • 案例-构造方法

  • 案例-调用构造方法

  • 案例-结果

  • 获取类的成员变量并使用

  • 需要注意的是:
  • 不带Declared修饰的可以获取父类的字段,带它修饰的不能获取到
  • (1)-批量的:
    • public Field[] getFields():获取所有的"公有字段"
    • public Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有
  • (2)-获取单个的
    • public Field getField(String fieldName):获取某个"公有的"字段
    • public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
  • (3)-设置字段的值
    • Field.set(Object obj,Object value)
    • public void set(Object obj,Object value)
    • 参数说明:
      • 1.obj:要设置的字段所在的对象
      • 2.value:要为字段设置的值
  • 案例-实体类

  • 案例-获得实体类属性并设置属性值

  • 案例-结果

  • 获取类的成员方法并使用

  • (1)-批量的:
  • public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
  • public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
  • (2)-获取单个的:
    • public Method getMethod(String name,Class<?>... parameterTypes)
    • 参数说明:
      • name : 方法名
      • Class ... : 形参的Class类型对象
    • public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
  • (3)-调用方法:
    • Method.invoke(Object obj,Object... args)
    • public Object invoke(Object obj,Object... args):
    • 参数说明:
      • obj : 要调用方法的对象
      • args:调用方式时所传递的实参
  • 方法和成员变量一样都有继承父类之说,两者都一样
  • 带有Declared修饰的都是不能包含父类的
  • 同时指定使用某个成员变量或者方法时要将变量名称和方法名称传入
  • 同时需要先实例化对象再使用
  • 案例-定义方法

  • 案例-调用方法

  • 案例-结果

  • 问题引入

  • 定义注解PersonInfoAnnotation

  • 定义注解CourseInfoAnnotation

  • 使用注解

  • 怎么操作ICPC里面的注解?
  • 解析注解,通过反射获得AnnotatedElement
  • 解析类的注解

  • 运行结果:

  • 解析成员变量的注解标签

  • 1、先获得类对象
  • 2、在获得该类上得所有成员变量
  • 3、遍历成员变量数组
  • 4、判定成员变量上有没有指定得注解
  • 5、有的话就获取注解标签实例,并打印出来

  • 运行结果:

  • 解析方法上的注解

  • 运行结果:

  • CLASS:注解出现在编译好的class文件里,但是注解的相关信息不能出现在运行时
  • 一旦修改注解的生命周期不为RUNTIME,那我们就无法通过反射来获取注解信息
  • 注解获取属性值的底层实现

  • JVM会为注解生成代理对象,注解其实也是一个接口,jvm会生成中间代理对象(在程序运行时生成称为动态代理对象)
  • 通过键值对的形式为注解属性赋值
  • 编译器检查注解的使用范围,将注解信息写入元素属性表
  • 运行时JVM将RUNTIME的所有注解属性取出并最终存入map里
  • 创建AnnotationInvocationHandler实例并传入前面的map
  • JVM使用JDK动态代理为注解生成代理类,并初始化处理器
  • 调用invoke方法,通过传入方法名返回注解对应的属性值