反射. 程序运行期间JVM会对任意一个类洞悉它的属性和方法,对任意一个对象都能够访问它的属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。

反射本身的用法很简单,但是背后牵扯的东西则不那么简单,从简单的使用入手,三言两语就可以说完了,但是牵扯到背后的东西,估计就得掉几根头发了,好在已经有前人分享了很多,可以少掉几根。

为什么我们需要反射

反射是实现一些只有动态语言才有的特性的关键。并非所有事情都可以在编译时确定,有时我们需要实例化一个类或仅根据文字字符串名称调用一个方法。
如果没有提供反射,开发人员就必须做一些像字节码工程这样的脏活。许多框架大量使用反射功能。耳熟能详的 Spring Mybatis , Fastjson 等等

反射的基本使用

获取一个对象实例

  1. /**
  2. * @author luobo.cs@raycloud.com
  3. * @since 2021/7/17 10:19 下午
  4. */
  5. public class ReflectTest {
  6. public static void main(String[] args) throws Exception {
  7. //Spring Bean的生命周期一环(). 首先实例化,然后是aware接口以及init等等方法
  8. final Class<?> clazz = Class.forName("java.lang.Object",//package.class
  9. true, ReflectTest.class.getClassLoader());
  10. final Object object = clazz.newInstance();
  11. System.out.println(object.toString());
  12. }
  13. }

访问一个字段

实例化一个java.util.ArrayList

  1. public class ReflectTest {
  2. @SuppressWarnings("unchecked")
  3. public static void main(String[] args) throws Exception {
  4. final Class<ArrayList<Object>> clazz = (Class<ArrayList<Object>>) Class.forName("java.util.ArrayList",
  5. true, ReflectTest.class.getClassLoader());
  6. final ArrayList<Object> object = clazz.newInstance();
  7. object.add("chenshun00");
  8. final Field elementData = object.getClass().getDeclaredField("elementData");
  9. elementData.setAccessible(true);
  10. final Object[] objects = (Object[]) elementData.get(object);
  11. System.out.println(Arrays.toString(objects));
  12. }
  13. }
  14. //output
  15. //[chenshun00, null, null, null, null, null, null, null, null, null]

访问一个方法

访问一个方法同上,不过getDeclaredField,换成了 getDeclaredMethod

  1. public class ReflectTest {
  2. @SuppressWarnings("unchecked")
  3. public static void main(String[] args) throws Exception {
  4. final Class<ArrayList<Object>> clazz = (Class<ArrayList<Object>>) Class.forName("java.util.ArrayList",
  5. true, ReflectTest.class.getClassLoader());
  6. final ArrayList<Object> object = clazz.newInstance();
  7. object.add("chenshun00");
  8. final Method method = object.getClass().getDeclaredMethod("contains",Object.class);
  9. method.setAccessible(true);
  10. final boolean result = (boolean) method.invoke(object, "chenshun00");
  11. System.out.println(result);
  12. }
  13. }
  14. //output --> true

构建构造函数

  1. package com.raycloud.api.common.core.http;
  2. /**
  3. * @author luobo.cs@raycloud.com
  4. * @since 2021/7/17 10:49 下午
  5. */
  6. public class Temp {
  7. public static Temp first = new Temp("first");
  8. public static Temp second = new Temp("second");
  9. private final String name;
  10. private Temp(String name) {
  11. this.name = name;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. }
  17. ------------------------------------------------------------------------
  18. package com.raycloud.api.common.core.http;
  19. import java.lang.reflect.Constructor;
  20. public class ReflectTest {
  21. @SuppressWarnings("unchecked")
  22. public static void main(String[] args) throws Exception {
  23. final Class<Temp> clazz = (Class<Temp>) Class.forName("com.raycloud.api.common.core.http.Temp",
  24. true, ReflectTest.class.getClassLoader());
  25. final Constructor<Temp> constructor = clazz.getDeclaredConstructor(String.class);
  26. constructor.setAccessible(true);
  27. final Temp temp = constructor.newInstance("chenshun00");
  28. System.out.println(temp.getName());
  29. }
  30. }

他们特点都是 setAccessible(true) ,如果不设置只能访问 public 的字段哦,如果设置了可以访问到 private 等等,如果不设置会出现如下错误.

Exception in thread “main” java.lang.IllegalAccessException: Class com.raycloud.api.common.core.http.ReflectTest can not access a member of class com.raycloud.api.common.core.http.Temp with modifiers “private”

引申: 反射引入动态性相对而言影响了系统等安全🔐,那么有没有什么方式,禁止反射对某一些Bean的操作呢?

反射依赖的是什么

反射可以在运行时动态获取class对象或实例,并且调用这些对象或实例的方法。

如此必然有一个数据的输入和输出,那么在调用反射的时候输入输出分别是什么. 从不同的层面可以给出不同的解释。

  • 加载输入的是class字节码文件,输出的是位于内存中的Class实例对象 把字节码文件加载Class的过程
  • 输入的是Class实例,输出的时候实例调用方法后的接口,具体反射API的应用.

这个过程就涉及以下这些问题。

  • A.class 文件,根据源代码生成的class文件. (Javac将源代码生成字节码class文件)
  • 如何加载class文件到JVM进程中 (从物理层面的字节码文件编程内存中的一个Class实例)
  • Class.forName(“package.Class”,true,ClassLoader) 的过程中发生了什么。
    • 加载class文件到JVM中—-> 如何验证呢? **-verbose**
    • classLoader用来干啥(类加载机制)
  • JVM是如何构建一个实例的。

这些问题随便扯一扯都可以扯上一天。

参考链接