![Spring快速入门](https://wfqqreader-1252317822.image.myqcloud.com/cover/410/31794410/b_31794410.jpg)
1.2 反射
本节首先介绍反射的基本概念,理解什么是反射,以及Class类和反射常用API,通过实例操作来学习反射的使用。
1.2.1 反射机制
在上面自定义注解时我们也有提到反射,要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象。那么什么是反射呢?在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。它有点类似照妖镜的作用,不管是什么妖魔鬼怪(类或对象)都能看到它的真面目(获取类的属性方法、调用对象的属性方法)。
1.2.2 理解Class类
反射机制可以动态获取类信息以及调用对象方法,那它是通过什么实现的呢?这就要介绍一下Class类了。首先明确Class也是一个类,只是它是一个描述类的类,也可以生成对象。对于每个类而言,在JRE中有且仅有一个不变的Class类型的对象,而这个Class类型的对象只能由系统建立,封装了当前对象所对应的类的信息,有哪些属性、方法、构造器以及实现了哪些接口等。每个类的实例都会记得自己是由哪个Class实例所生成的。
要获取类信息或调用对象方法,肯定首先要获取到该类或对象对应的Class类的实例。一般获取Class对象有3种方式。
● 通过类名获取,类名.class。
● 通过对象获取,对象.getClass()。
● 通过全类名获取,Class.forName(全类名)。
这里我们可以使用字符串来做验证。
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer139.jpg?sign=1738887467-YQNUmoNCsm9h2N2A0xhhcgCTrARv36mp-0-d3e99454dec024ff87afdb2decbfd0c3)
输出结果:
class java.lang.String class java.lang.String class java.lang.String
通过3种方式获取到Class实例后,再了解一下Class类常用的方法(见表1-1)。
表1-1 Class类常用的方法
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer423.jpg?sign=1738887467-MfK9YL5JrM6shsY2B2QONdwWGkxTsX0R-0-7e90e72fcc734185f0295fa74f3c5243)
1.2.3 反射的使用
这里要着重介绍一下上面API的使用,因为在后面要学习的Spring中IOC的原理就是反射加工厂模式。学好反射API有助于理解Spring框架内部实现。为了演示Class方法的使用,在注解demo的基础上对Person、Student类进行了修改。
Person类:
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer155.jpg?sign=1738887467-Spb0DCwFsGYs9036jTbQ61xUwmID8dVy-0-4d7f6f6f94f312783dbcb028a42cca54)
Student类:
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer172.jpg?sign=1738887467-po98QoyhQvpcuzAXlLNmL1b2v8TEFfcG-0-887396fc81a13fc92e563fdca33ffb81)
1.描述方法Method
描述方法主要是4个获取方法(getMethods、getMethod、getDeclaredMethods、getDeclaredMethod)和1个调用方法(invoke)。
● getMethods:获取clazz对应类中的所有方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。
● getMethod:获取clazz对应类中指定方法名和参数类型的方法,不能获取private方法,且获取从父类继承来的所有方法,包括私有父类的私有方法。因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法。
● getDeclaredMethods:获取所有方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。
● getDeclaredMethod:获取clazz对应类中指定方法名和参数类型的方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法。
● invoke:执行方法,第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数,私有方法的执行必须在调用invoke之前加上一句“method.setAccessible(true);”。
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer187.jpg?sign=1738887467-IaYgjzxdodcw1QoENy5UmlwI8MjcWafZ-0-3029d6fd079d2f62b725b3597a1263ac)
输出结果:
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer188.jpg?sign=1738887467-cIq0enKSjwpdzBvPdnvqhBG6sCvmC9bK-0-0def2460da1106941f8fe389e109eecc)
上面我们基本可以实现通过类名创建对象、通过方法名执行方法。类名和方法名都是字符串,我们可以把它们放到一个配置文件中,根据配置文件来执行方法,这样就有点类似基于XML的Spring了。
2.描述字段Field
描述字段Field方法的使用和描述方法Method中方法的使用有点类似,也是4个获取字段的方法(getFields、getField、getDeclaredFields、getDeclaredField)。
● getFields:获得某个类的所有公共(public)字段,包括父类中的字段。
● getField:获取某个类public成员变量中指定变量名的字段,包括基类。
● getDeclaredFields:获得某个类所有声明的字段,包括public、private和protected,但是不包括父类的声明字段。
● getDeclaredField:获取某个类的所有成员变量指定变量名的字段,不包括基类。
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer213.jpg?sign=1738887467-LdVyEUNateNJgP6A5iTL8JNaupOnyss7-0-8c8e6f886ee442a3b8320ebdfbd879bf)
输出结果:
---------getDeclaredFields--------- StudentId ---------getFields--------- StudentId ---------getDeclaredField--------- StudentId ---------getField-------- StudentId
上面通过反射获取字段,得到字段之后就是获取或设置字段的值了。如果字段是私有的,那么不管是读值还是写值,都必须先调用setAccessible(true)方法,比如在Person类中,字段name字段是私有的。
Class clazz = Class.forName("Reflection.Person"); Person person = new Person("CYW"); //获取私有字段的值 Field field = clazz.getDeclaredField("Name"); //由于是私有字段,因此需要使用setAccessible(true) field.setAccessible(true); Object val = field.get(person); System.out.println(val); //改变私有字段的值 field.set(person, "ivan"); System.out.println(person.getName());
输出结果:
CYW ivan
3.描述构造器Constructor
先介绍一下描述构造函数Constructor用到的方法,主要还是4个:getConstructors、getDeclaredConstructors、getConstructor、getDeclaredConstructor。和前面Method、Field用的方法进行比较,举一反三,我们也能大概了解这几个方法的使用。其实,在编程中有好多体现哲学思想的地方,有正有反,有阴有阳,学会思考,这样可以以点带面、触类旁通。
● getConstructors:获取对应类中public类型的构造函数,且只获取当前类的构造函数。
● getConstructor:获取对应类中public指定参数类型的构造函数,且只获取当前类的构造函数。
● getDeclaredConstructors:获取对应类中所有构造函数,包括私有构造函数,且只获取当前类的构造函数。
● getDeclaredConstructor:获取对应类中指定参数类型的方法,包括私有构造函数,且只获取当前类的方法。
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer256.jpg?sign=1738887467-vrj5PoTbqCjpLC8xZW5h7tN1CnaY5qe8-0-7d0746790a633e54b4b2019d6ea555bf)
输出结果:
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer258.jpg?sign=1738887467-wIbHXjvQGi5fx92lMPBEzuD6FRnXzdKN-0-27c093c3a9c48b673d88918f247bc8e4)
4.描述注解Annotation
描述注解主要用到getAnnotation(Class<A> annotationClass)方法,返回该元素指定类型的注解,否则返回null。
![](https://epubservercos.yuewen.com/651929/17214367405510906/epubprivate/OEBPS/Images/figer257.jpg?sign=1738887467-jrc6rk4AbwojwMgrY5kjt93v2BeP40dd-0-659c407948d5ab4b8079d539dba9302b)
输出结果:
description:学生 description:人