反射是指程序可以访问,检测,修改它本身状态或行为的一种能力。
java 的反射机制是指在程序运行状态中,给定任意一个类,都可以获取到这个类的属性和方法;给定任意一个对象都可以调用这个对象的属性和方法,这种动态的获取类的信息和调用对象的方法的功能称之为 java 的反射机制。 一言以蔽之:反射机制可以让你在程序运行时,拿到任意一个类的属性和方法并调用它。
想要理解反射首先需要知道 Class 这个类,它的全称是 java.lang.Class 类。java 是面向对象的语言,讲究万物皆对象,即使强大到一个类,它依然是另一个类(Class 类)的对象,换句话说,普通类是 Class 类的对象,即 Class 是所有类的类(There is a class named Class)。 对于普通的对象,我们一般会这样创建:
- Code code1 = new Code();
上面说了,所有的类都是 Class 的对象,那么如何表示呢,可不可以通过如下方式呢:
- Class c = new Class();
但是我们查看 Class 的源码时,是这样写的:
- private Class(ClassLoader loader) {
- classLoader = loader;
- }
可以看到构造器是私有的,只有 JVM 才可以调用这个构造函数创建 Class 的对象,因此不可以像普通类一样 new 一个 Class 对象,虽然我们不能 new 一个 Class 对象,但是却可以通过已有的类得到一个 Class 对象,共有三种方式,如下:
- Class c1 = Test.class; 这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的
- Class c2 = test.getClass(); test是Test类的一个对象,这种方式是通过一个类的对象的getClass()方法获得的
- Class c3 = Class.forName("com.catchu.me.reflect.Test"); 这种方法是Class类调用forName方法,通过一个类的全量限定名获得
这里,c1、c2、c3 都是 Class 的对象,他们是完全一样的,而且有个学名,叫做 Test 的类类型(class type)。 这里就让人奇怪了,前面不是说 Test 是 Class 的对象吗,而 c1、c2、c3 也是 Class 的对象,那么 Test 和 c1、c2、c3 不就一样了吗?为什么还叫 Test 什么类类型?这里不要纠结于它们是否相同,只要理解类类型是干什么的就好了,顾名思义,类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。 示例代码:
- public class Test {
- public static void main(String[] args) throws ClassNotFoundException {
- Class < Test > class1 = Test.class;
- System.out.println("类名1:" + class1.getName());
- Test Test = new Test();
- Class < ?extends Test > class2 = Test.getClass();
- System.out.println("类名2:" + class2.getName());
- Class < ?>class3 = Class.forName("com.catchu.me.reflect.Test");
- System.out.println("类名3:" + class3.getName());
- if (class1 == class2) {
- System.out.println("class1==class2");
- }
- if (class1 == class3) {
- System.out.println("class1==class3");
- }
- }
- }
输出结果:
- 类名1:com.catchu.me.reflect.Test
- 类名2:com.catchu.me.reflect.Test
- 类名3:com.catchu.me.reflect.Test
- class1==class2
- class1==class3
java 的反射操作主要是用到了 java.lang.Class 类和 java.lang.reflect 反射包下的类,上面说到我们已经可以拿到一个类的 Class 信息,根据这个 Class 我们就可以使用某些方法来操作(获取)类的以下信息:
万物皆对象,类的构造函数是 java.lang.reflect.Constructor 类的对象,通过 Class 的下列方法可以获取构造函数对象:
- public Constructor < T > getDeclaredConstructor(Class < ?>...parameterTypes) // 获得该类所有的构造器,不包括其父类的构造器
- public Constructor < T > getConstructor(Class < ?>...parameterTypes) // 获得该类所有public构造器,包括父类
- //具体
- Constructor < ?>[] allConstructors = class1.getDeclaredConstructors(); //获取class对象的所有声明构造函数
- Constructor < ?>[] publicConstructors = class1.getConstructors(); //获取class对象public构造函数
- Constructor < ?>constructor = class1.getDeclaredConstructor(String.class); //获取指定声明构造函数(局部变量是一个字符串类型的)
- Constructor publicConstructor = class1.getConstructor(String.class); //获取指定声明的public构造函数
测试代码如下:
- public class TestConstructor {
- public static void main(String[] args) throws Exception {
- Class < ?>personClass = Class.forName("com.catchu.me.reflect.Person");
- //获取所有的构造函数,包括私有的,不包括父类的
- Constructor < ?>[] allConstructors = personClass.getDeclaredConstructors();
- //获取所有公有的构造函数,包括父类的
- Constructor < ?>[] publicConstructors = personClass.getConstructors();
- System.out.println("遍历之后的构造函数:");
- for (Constructor c1: allConstructors) {
- System.out.println(c1);
- }
- Constructor < ?>c2 = personClass.getDeclaredConstructor(String.class);
- c2.setAccessible(true); //设置是否可访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不用设置
- Object person = c2.newInstance("刘俊重"); //使用反射创建Person类的对象,并传入参数
- System.out.println(person.toString());
- }
- }
Person 类如下,为测出效果包含一个私有构造函数:
- public class Person {
- private int age;
- private String name;
- public Person() {
- }
- private Person(String name){
- this.name = name;
- }
- public Person(int age,String name){
- this.age = age;
- this.name = name;
- }
- //省略set/get方法
- @Override
- public String toString() {
- return "Person{" +
- "age=" + age +
- ", name='" + name + '\'' +
- '}';
- }
- }
测试结果如下:
- 遍历之后的构造函数:
- public com.catchu.me.reflect.Person(int,java.lang.String)
- private com.catchu.me.reflect.Person(java.lang.String)
- public com.catchu.me.reflect.Person()
- Person{age=0, name='刘俊重'}
由上面可以看到我们在获得某个类的 Class 类类型之后,可以通过反射包中的方法获取到这个类的构造函数,进而可以创建该类的对象。
万物皆对象,类的成员变量是 java.lang.reflect.Field 类的对象,通过 Class 类的以下方法可以获取某个类的成员变量,值得一提的是变量是包含两部分的,变量类型和变量名:
- public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
- public Field getField(String name) // 获得该类自所有的public成员变量,包括其父类变量
- //具体实现
- Field[] allFields = class1.getDeclaredFields(); //获取class对象的所有属性
- Field[] publicFields = class1.getFields(); //获取class对象的public属性
- Field ageField = class1.getDeclaredField("age"); //获取class指定属性
- Field desField = class1.getField("des"); //获取class指定的public属性
示例代码如下:
- public class TestField {
- public static void main(String[] args) throws Exception {
- Class < Person > personClass = Person.class;
- //获取所有的成员变量,包含私有的
- Field[] allFields = personClass.getDeclaredFields();
- //获取所有公有的成员变量,包含父类的
- Field[] publicFields = personClass.getFields();
- System.out.println("所有的成员变量:");
- for (Field f: allFields) {
- System.out.println(f);
- }
- //获取某个变量的值
- //创建对象的实例
- Constructor < Person > c = personClass.getDeclaredConstructor(String.class);
- c.setAccessible(true); //因为该构造函数时私有的,需要在这里设置成可访问的
- Person person = c.newInstance("刘俊重");
- //获取变量name对象
- Field field = personClass.getDeclaredField("name");
- field.setAccessible(true); //因为变量name是私有的,需要在这里设置成可访问的
- //注意对比下面这两行,官方对field.get(Object obj)方法的解释是返回对象obj字段field的值
- Object value = field.get(person);
- //String name = person.getName();
- System.out.println("获取的变量的值是:" + value);
- }
- }
输出结果如下:
- 所有的成员变量:
- private int com.catchu.me.reflect.Person.age
- private java.lang.String com.catchu.me.reflect.Person.name
- 获取的变量的值是:刘俊重
这里要注意 field.get(person) 方法,我们根据对象获取属性的常规方法是通过:String name = person.getName(),反射中可以通过:字段. get(对象),这也是获取对象的某个字段,有点类似于 invoke 方法。
万物皆对象,类的成员方法是 java.lang.reflect.Method 的对象,通过 java.lang.Class 类的以下方法可以获取到类的成员方法,通过方法类 Method 提供的一些方法,又可以调用获取到的成员方法。
- public Method getDeclaredMethod(String name, Class < ?>...parameterTypes) // 得到该类所有的方法,不包括父类的
- public Method getMethod(String name, Class < ?>...parameterTypes) // 得到该类所有的public方法,包括父类的
- //具体使用
- Method[] methods = class1.getDeclaredMethods(); //获取class对象的所有声明方法
- Method[] allMethods = class1.getMethods(); //获取class对象的所有public方法 包括父类的方法
- Method method = class1.getMethod("info", String.class); //返回此class1对应的public修饰的方法名是info的,包含一个String类型变量的方法
- Method declaredMethod = class1.getDeclaredMethod("info", String.class); //返回此Class对象对应类的、带指定形参列表的方法
测试代码如下:
- public class TestMethod {
- public static void main(String[] args) throws Exception {
- Person person = new Person();
- Class < ?extends Person > personClass = person.getClass();
- Method[] allMethods = personClass.getDeclaredMethods();
- Method[] publicMethods = personClass.getMethods();
- System.out.println("遍历所有的方法:");
- for (Method m: allMethods) {
- System.out.println(m);
- }
- //下面是测试通过反射调用函数
- //通过反射创建实例对象,默认调无参构造函数
- Person person2 = personClass.newInstance();
- //获取要调用的方法,要调用study方法,包含int和String参数,注意int和Integer在这有区别
- Method method = personClass.getMethod("study", int.class, String.class);
- Object o = method.invoke(person2, 18, "刘俊重");
- }
- }
测试结果:
- 遍历所有的方法:
- public java.lang.String com.catchu.me.reflect.Person.toString()
- public java.lang.String com.catchu.me.reflect.Person.getName()
- public void com.catchu.me.reflect.Person.setName(java.lang.String)
- public void com.catchu.me.reflect.Person.study(int,java.lang.String)
- public int com.catchu.me.reflect.Person.getAge()
- public void com.catchu.me.reflect.Person.setAge(int)
- 我叫刘俊重,我今年18,我在学习反射
注意:Object o = method.invoke(person2, 18," 刘俊重 "); 就是调用 person2 对象的 method 方法,格式是:方法名. invoke(对象, 参数), 类似于获取成员变量值时的 get 方法。 由上面可以看出反射的强大:通过反射我们可以获取到类类型,通过 Class 类型我们可以获取到构造函数,进而实例化 new 出一个对象;通过反射我们可以获取到成员变量和成员方法,通过实例出的对象又可以获取到这些成员变量的值或调用成员方法。这才只是反射的一部分,通过反射我们还可以判断类,变量,方法,是否包含某些特定注解,还可以通过反射来动态代理去调用其它方法,跟注解和动态代理挂起勾会有无限的想象空间,比如 spring 框架,底层就是通过这些原理。下面在说几个反射常用的 API,最后会介绍反射跟注解和动态代理的结合使用。
- Annotation[] annotations = (Annotation[]) class1.getAnnotations();//获取class对象的所有注解
- Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);//获取class对象指定注解
- Type genericSuperclass = class1.getGenericSuperclass();//获取class对象的直接超类的
- Type Type[] interfaceTypes = class1.getGenericInterfaces();//获取class对象的所有接口的type集合
- boolean isPrimitive = class1.isPrimitive(); //判断是否是基础类型
- boolean isArray = class1.isArray(); //判断是否是集合类
- boolean isAnnotation = class1.isAnnotation(); //判断是否是注解类
- boolean isInterface = class1.isInterface(); //判断是否是接口类
- boolean isEnum = class1.isEnum(); //判断是否是枚举类
- boolean isAnonymousClass = class1.isAnonymousClass(); //判断是否是匿名内部类
- boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class); //判断是否被某个注解类修饰
- String className = class1.getName(); //获取class名字 包含包名路径
- Package aPackage = class1.getPackage(); //获取class的包信息
- String simpleName = class1.getSimpleName(); //获取class类名
- int modifiers = class1.getModifiers(); //获取class访问权限
- Class < ?>[] declaredClasses = class1.getDeclaredClasses(); //内部类
- Class < ?>declaringClass = class1.getDeclaringClass(); //外部类
- ClassLoader ClassLoader = class1.getClassLoader()返回类加载器
- getSuperclass():获取某类所有的父类getInterfaces():获取某类所有实现的接口
代理的操作是通过 java.lang.reflect.Proxy 类中实现的,通过 Proxy 的 newProxyInstance() 方法可以创建一个代理对象,如下:
- public static Object newProxyInstance(ClassLoader loader, 类 < ?>[] interfaces, InvocationHandler h)
不要看到这里面一大坨晦涩的屎代码就害怕,这里面是有技巧的,其实都是模板,需要什么,我们传什么过去就可以了。可以看到需要三个参数,类加载器,接口和调用处理者。我们在上面已经能拿到 Class 类了,使用 class.getClassLoader 就可以获取类加载器,使用 class.getgetInterfaces() 可以获取所有的接口,那现在要写的不就是新建一个 InvocationHandler 对象了吗?事实上,我们动态代理的核心代码也就是在这里面写的。我上面说的模板,其实就是下面这几步:
- public interface PersonInterface {
- void doSomething();
- void saySomething();
- }
接口的实现类:
- public class PersonImpl implements PersonInterface {
- @Override
- public void doSomething() {
- System.out.println("人类在做事");
- }
- @Override
- public void saySomething() {
- System.out.println("人类在说话");
- }
- }
代理类:
- /**
- * @author 刘俊重
- */
- public class PersonProxy {
- public static void main(String[] args) {
- final PersonImpl person = new PersonImpl();
- PersonInterface proxyPerson = (PersonInterface) Proxy.newProxyInstance(PersonImpl.class.getClassLoader(), PersonImpl.class.getInterfaces(), new InvocationHandler() {
- //在下面的invoke方法里面写我们的业务
- @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if (method.getName() == "doSomething") {
- person.doSomething();
- System.out.println("通过常规方法调用了实现类");
- } else {
- method.invoke(person, args);
- System.out.println("通过反射机制调用了实现类");
- }
- return null;
- }
- });
- proxyPerson.doSomething();
- proxyPerson.saySomething();
- }
- }
执行结果如下:
- 人类在做事
- 通过常规方法调用了实现类
- 人类在说话
- 通过反射机制调用了实现类
在我们通过 proxyPerson.doSomething() 调用的时候,其实不是立马进入实现类的 doSomething 方法,而是带着方法名,参数进入到了我们的代理方法 invoke 里面,在这里面我进行了一次判断,如果等于"doSomething"就使用常规方法调用,否则使用反射的方法调用。这样看似还是平时的调用,但是每次执行都要走我们的代理方法里面,我们可以在这里面做些" 手脚 ",加入我们的业务处理。 可以看下面另一个示例,比如天猫一件衣服正常卖 50,现在你是我的 vip 用户,可以给你打折扣 10 块,其它业务都是相同的,只有这里便宜了 10,重新服务提供者就很麻烦,用代理可以解决这个问题。 接口 SaleService:
- public interface SaleService {
- //根据尺码返回衣服的大小
- int clothes(String size);
- }
接口实现类 SaleServiceImpl:
- public class SaleServiceImpl implements SaleService {
- @Override
- public int clothes(String size) {
- System.out.println("衣服大小"+size);
- //模拟从数据库取衣服价格
- return 50;
- }
- }
普通无折扣的调用测试:
- /**
- * @author 刘俊重
- * @Description 普通用户
- */
- public class OrdinaryCustom {
- public static void main(String[] args) {
- SaleService saleService = new SaleServiceImpl();
- int money = saleService.clothes("XXl");
- System.out.println("价格是:" + money);
- }
- }
输出结果:
- 衣服大小XXl
- 价格是:50
代理类 ProxySale:
- /**
- * @author 刘俊重
- * @Description 代理类
- */
- public class ProxySale {
- //对接口方法进行代理
- public static <T> T getProxy(final int discount, final Class<SaleServiceImpl> implementClasses, Class<SaleService> interfaceClasses){
- return (T)Proxy.newProxyInstance(implementClasses.getClassLoader(),
- implementClasses.getInterfaces(), new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //调用原始对象的方法,获取未打折之前的价格
- int price = (int) method.invoke(implementClasses.newInstance(), args);
- return price-discount;
- }
- });
- }
- }
vip 用户测试类 VipCustom:
- /**
- * @author 刘俊重
- * @Description Vip用户,有打折优惠
- */
- public class VipCustom {
- public static void main(String[] args) {
- //vip用户,打10元折扣
- int discount = 10;
- SaleService saleService = ProxySale.getProxy(discount, SaleServiceImpl.class, SaleService.class);
- int money = saleService.clothes("xxl");
- System.out.println("价格是:" + money);
- }
- }
输出结果是:
- 衣服大小xxl
- 价格是:40
可以看到,在未修改服务提供者的情况下,我们在代理类里面做了手脚,结果符合预期。
- @Override
- void myMethod() {
- ......
- }
这其中 @Override 就是注解。这个注解的作用也就是告诉编译器,myMethod() 方法覆盖了父类中的 myMethod() 方法。
- @Override:表示当前的方法定义将覆盖超类中的方法,如果出现错误,编译器就会报错。
- @Deprecated:如果使用此注解,编译器会出现警告信息。
- @SuppressWarnings:忽略编译器的警告信息。
- @Target
- @Retention
- @Documented
- @Inherited
java8 加了两个新注解,后续我会讲到。
@Target 说明了 Annotation 所修饰的对象范围:Annotation 可被用于 packages、types(类、接口、枚举、Annotation 类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch 参数)。在 Annotation 类型的声明中使用了 target 可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方) 取值 (ElementType) 有:
类型 | 用途 |
---|---|
CONSTRUCTOR | 用于描述构造器 |
FIELD | 用于描述域 |
LOCAL_VARIABLE | 用于描述局部变量 |
METHOD | 用于描述方法 |
PACKAGE | 用于描述包 |
PARAMETER | 用于描述参数 |
TYPE | 用于描述类、接口 (包括注解类型) 或 enum 声明 |
比如定义下面一个注解,它就只能用在方法上,因为已经限定了它是方法级别的注解,如果用在类或者其它上面,编译阶段就会报错:
- @Target({
- ElementType.METHOD
- }) public@interface MyMethodAnnotation {}
测试类 MyClass:
- //@MyMethodAnnotation 报错,方法级别注解不能注在类头上
- public class MyClass {
- @MyMethodAnnotation
- public void myTestMethod(){
- //
- }
- }
@Retention 定义了该 Annotation 被保留的时间长短:某些 Annotation 仅出现在源代码中,而被编译器丢弃;而另一些却被编译在 class 文件中;编译在 class 文件中的 Annotation 可能会被虚拟机忽略,而另一些在 class 被装载时将被读取(请注意并不影响 class 的执行,因为 Annotation 与 class 在使用上是被分离的)。使用这个 meta-Annotation 可以对 Annotation 的 "生命周期" 限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效) 取值(RetentionPoicy)有:
类型 | 用途 | 说明 |
---|---|---|
SOURCE | 在源文件中有效(即源文件保留) | 仅出现在源代码中,而被编译器丢弃 |
CLASS | 在 class 文件中有效(即 class 保留) | 被编译在 class 文件中 |
RUNTIME | 在运行时有效(即运行时保留) | 编译在 class 文件中 |
示例:
- @Target({ElementType.TYPE}) //用在描述类、接口或enum
- @Retention(RetentionPolicy.RUNTIME) //运行时有效
- public @interface MyClassAnnotation {
- String value(); //这个MyClassAnnotation注解有个value属性,将来可以设置/获取值
- }
@Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共 API,因此可以被例如 javadoc 此类的工具文档化。Documented 是一个标记注解,没有成员。
作用:将注解包含在 javadoc 中
- java.lang.annotation.Documented
- @Documented
- public @interface MyCustomAnnotation { //Annotation body}
是一个标记注解,阐述了某个被标注的类型是被继承的,使用了 @Inherited 修饰的 annotation 类型被用于一个 class, 则这个 annotation 将被用于该 class 的子类,@Inherited annotation 类型是被标注过的 class 的子类所继承。类并不从实现的接口继承 annotation, 方法不从它所重载的方法继承 annotation,当 @Inherited annotation 类型标注的 annotation 的 Retention 是 RetentionPolicy.RUNTIME,则反射 API 增强了这种继承性。如果我们使用 java.lang.reflect 去查询一个 @Inherited annotation 类型的 annotation 时,反射代码检查将展开工作:检查 class 和其父类,直到发现指定的 annotation 类型被发现,或者到达类继承结构的顶层。
作用:允许子类继承父类中的注解 示例,这里的 MyParentClass 使用的注解标注了 @Inherited,所以子类可以继承这个注解信息:
- java.lang.annotation.Inherited
- @Inherited
- public @interface MyCustomAnnotation {
- }
- @MyCustomAnnotation
- public class MyParentClass {
- ...
- }
- public class MyChildClass extends MyParentClass {
- ...
- }
格式
- public @interface 注解名{
- 定义体
- }
示例:
- @Target(ElementType.FIELD)
- @Retention(value=RetentionPolicy.RUNTIME)
- @Documented
- public @interface MyFieldAnnotation {
- int id() default 0;
- String name() default "";
- }
定义了一个用在字段上的,运行时有效的名为 MyFieldAnnotation 的注解,它有两个属性,int 类型的 id(id 后面记得带括号)默认值是 0,还有一个 String 类型的 name,默认值是 ""。
在上面我们已经知道了怎么自定义一个注解了,但是光定义没用啊,重要的是我要使用它,使用的方法也很简单,最上面讲反射的时候也提到过几个这样的方法了,比如:class1.isAnnotation(),其实他们统统是 java.lang.reflect 包下的 AnnotatedElement 接口里面的方法,这个接口主要有以下几个实现类:
测试代码 CustomClassAnnotation 如下:
- /**
- * @author 刘俊重
- * @Description 自定义类注解
- */
- @Target(ElementType.TYPE) //作用在类,枚举或接口上
- @Retention(RetentionPolicy.RUNTIME) //运行时有效
- @Documented //文档可见
- public@interface CustomClassAnnotation {
- String value(); //获取注解名称
- }
FruitName 类如下:
- /**
- * @author 刘俊重
- * @Description 字段注解(字符串类型的)
- */
- @Target(ElementType.FIELD) //用在字段上
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface FruitName {
- String name() default "";
- }
FruitColor 类如下:
- /**
- * @author 刘俊重
- * @Description 字段注解(枚举类型的)
- */
- @Target(ElementType.FIELD)
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface FruitColor {
- //颜色枚举
- enum Color{BLUE,RED,GREEN};
- //颜色属性
- Color color() default Color.RED;
- }
Fruit 实体类如下:
- /**
- * @author 刘俊重
- * @Description Fruit实体类
- */
- @CustomClassAnnotation(value="fruit")
- public class Fruit{
- @FruitName(name="apple")
- private String name;
- @FruitColor(color= FruitColor.Color.RED)
- private String color;
- }
测试类 TestAnnotation 如下:
- /**
- * @author 刘俊重
- * @Description 测试类
- */
- public class TestAnnotation {
- public static void main(String[] args) {
- Class < Fruit > clazz = Fruit.class; //反射获取Class对象
- CustomClassAnnotation annotation = clazz.getAnnotation(CustomClassAnnotation.class); //拿到Fruit类的注解
- if (null != annotation && "fruit".equals(annotation.value())) {
- System.out.println("Fruit类的注解名是======" + annotation.value());
- //获取所有的属性遍历,拿到每一个属性的值
- Field[] allFields = clazz.getDeclaredFields();
- for (Field field: allFields) {
- if (field.isAnnotationPresent(FruitName.class)) {
- //判断是否存在FruitName注解
- FruitName fruitName = field.getAnnotation(FruitName.class);
- System.out.println("水果名称=====" + fruitName.name());
- }
- if (field.isAnnotationPresent(FruitColor.class)) {
- FruitColor fruitColor = field.getAnnotation(FruitColor.class);
- System.out.println("水果颜色=====" + fruitColor.color());
- }
- }
- } else {
- System.out.println("注解值不对,请检查");
- }
- }
- }
总结:通过注解可以获取到类名,接口名,方法名,属性名,再搭配反射可以动态的生成对象,再搭配动态代理,去动态的去调用某些方法,这基本上也就是 spring 框架底层实现原理的一部分了。 注:本文一些内容引用自 http://t.cn/RK8ci8w ,原作者反射和注解写的确实不错,我对部分内容进行了完善并重写了部分示例;动态代理部分我加的,考虑着把反射,代理,注解放在一块来说。
附一下个人微信公众号,欢迎跟我交流。
来源: https://juejin.im/post/5a44c0ad518825455f2f96e5