b04-2 spring容器相关java知识-反射(精通Spring 4.x)

本篇内容:

  • 1、java反射实现
  • 2、java类加载器ClassLoader
  • 3、java反射机制

一、java反射代码

1、创建java实体类

public class Car {
    private String brand;
    private String color;
    private int maxSpeed;
    public Car() {
    }
    public Car(String brand, String color, int maxSpeed) {
        this.brand = brand;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
    public void getData() {
        System.out.println("Car{" +
                "brand='" + brand + '\'' +
                ", color='" + color + '\'' +
                ", maxSpeed=" + maxSpeed +
                '}');
    }
    //    省略get,set方法
}

2、测试类:通过反射获取java对象

public class CarTest {
    public static Car initByDefaultConst() throws Exception {
        //得到ClassLoader对象
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        //通过java全路径名称获取java实体对象
        Class aClass = loader.loadClass("com.demo.Car");
        //获取反射对
        Constructor constructor = aClass.getDeclaredConstructor(null);
        //通过构造函数实例化java对象
        Car car = (Car) constructor.newInstance();
        //通过类对象获取实体的setter方法
        Method setBrand = aClass.getMethod("setBrand", String.class);
        setBrand.invoke(car, "雷克萨斯 ES");
        Method setColor = aClass.getMethod("setColor", String.class);
        setColor.invoke(car, "银色");
        Method setMaxSpeed = aClass.getMethod("setMaxSpeed", int.class);
        setMaxSpeed.invoke(car, 300);
        return car;
    }
    @Test
    public void getCar() throws Exception {
        Car car = initByDefaultConst();
        car.getData();
    }
}

二、java的类加载器ClassLoader

1、工作机制

  • 寻找类的字节码文件并构造出类在JVM内部表示对象的组件。

类加载器把一个类加入jvm的步骤

  • 1、装载:查找和导入Class文件

  • 2、连接:执行校验、准备和解析步骤(解析步骤是可选的)

  • 2.1、校验:检查载入Class文件数据的准确性

  • 2.2、准备:给类的静态变量分配存储空间

  • 2.3、解析:将符号引用转换成直接引用

  • 3、初始化:对类的静态变量,静态代码块执行初始化工作

  • 类的装载构造由ClassLoader及其子类负责,ClassLoader是一个重要的java运行时系统组件,负责在运行时查找和装入class字节码文件

  • JVM在运行时会产生3个ClassLoader:根装载器,ExtClassLoader(扩展类装载器),APPClassLoader(应用类装载器)

2、测试jvm中3个类的关系

    @Test
    public void getJVM(){
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        System.out.println("current: "+loader);
        System.out.println("current: "+loader.getParent());
        System.out.println("current: "+loader.getParent().getParent());
    }
    
  • 输出
current: sun.misc.Launcher$AppClassLoader@18b4aac2
current: sun.misc.Launcher$ExtClassLoader@776ec8df
current: null
  • 从输出内容可知:
  • 1、ClassLoader就是AppClassLoader,
  • 2、ClassLoader的父类是ExtClassLoader

jvm装载类时使用“全盘负责委托机制”。

  • 1、“全盘负责”:是指当一个ClassLoader装载一个类时,除非显示地使用另一个ClassLoader,该类所依赖及引用的类也由这个类载入。
  • 2、“委托机制”:是指先委托父类装载器找目标类,只有在找不到的情况下才从自己的类路径中查找并装载目标。

3、ClassLoader的重要方法

1、Class loadClass(String name)

  • name参数指定类装载需要的类名,必须使用全路径名称。重载方法Class loadClass(String name,boolean resolve)resolve参数告诉类装载器是否需要解析该类,在初始化前要考虑进行解析的工作,单不是所有的类都需要解析。

2、Class defuneClass(String name, byte[ ] b, int off, int len)

  • 将类文件的字节数组转换成JVM内部的Class对象,字节数组可以从本地文件系统,远程网络获取。参数name为全路径类名。

3、Class findSystemClass(String name)

  • 将本地文件系统载入Class文件,如果本地文件不存在该Class文件,将抛出ClassNotFoundException,该方法是JVM默认使用的装载机制

4、ClassLoader getParent()

  • 获取父类装载器,处理根装载器外所有的类装载器只有一个父装载器。ExtClassLoader的父装载器是根装载器,因为跟装载器不是java语言编写,无法获取
每个类在JVM中都拥有一个对应的Class对象,它提供了类结构信息的描述。Class对象是在装载类时由JVM通过调用类装载器中的defineClass()自动构造。

三、java反射机制

  • Class反射对象描述类语义结构,可以从Class对象中获取构造函数,成员变量,方法类等元素的反射对象,并以编程的方式通过这些反射对象对目标进行操作。
  • java主要反射类:Constructor,Method,Field

1、Constructor: 类的构造函数反射类

  • 通过Class#getConstructor()可以获取类的所有构造函数反射对象数组。
  • Constructor的一个主要方法是newInstance(Object[ ] initargs),通过此方法可以创建一个对象类的实例

2、Method:类方法的反射类

  • 通过Class#getDecrlaredMethods()可以获取类的所有方法反射类对象数组Method[ ]。
  • Method最主要的方法是incoke(Object obj,Object[ ] args),obj表示操作的目标对象,args为方法参数

Method用于获取类信息方法

  • Class getReturnType():获取方法的返回值类型
  • Class[ ] getParameterTypes():获取方法入参类型数组
  • Class[ ] getExceptionType():获取方法异常类型数组
  • Annotation[ ] [ ] getParameterAnnotations():获取方法的注解信息

3、Field:类的成员变量的反射类

  • 通过Class#getDeclaredFields()可以获取类的成员变量反射对象数组。
  • Class#getDeclaredField(String name):可以获取某个特定名称的成员变量反射对象
  • 主要方法:set(Object obj,Object value),obj表示操作的目标对象,value为设置值
  • 如果成员变量为基础类型,可以使用Fiel提供的带类型名的值设置方法:setBoolean(Object obj,boolean value),setInt(Object obj,int value)

4、私有方法反射

  • java为包提供了Package反射类,有注解反射类:AnnotatedElement反射类。
  • java的反射体系保证了可以通过程序化的方式访问目标类中所有的元素,对private或protected成员变量和方法,只要JVM安全机制允许也可以通过反射调用

4.1、定义实体类

public class PrivateCar {
    private String color;
    protected void getData() {
        System.out.println("the color is :" + color);
    }
}

4.2、测试类

public class PrivateCarTest {
    @Test
    public void getCar() throws Exception {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class clazz = loader.loadClass("com.demo.PrivateCar");
        PrivateCar car = (PrivateCar) clazz.newInstance();
        Field colorField = clazz.getDeclaredField("color");
        colorField.setAccessible(true);     //取消java语言访问检查
        colorField.set(car,"银色");
        Method method = clazz.getDeclaredMethod("getData", null);
        method.setAccessible(true);     //取消java语言访问检查
        method.invoke(car,null);
    }
}

注意:

  • 访问private或protected修饰的对象或方法需要调用setAccessible(boolean access)取消java语言检查,否则抛出异常。

demo代码地址


https://github.com/brusion/brusion-code/tree/master/demo-java/03%20-%20demo%20-%20spring/spring-study-4x/01-ioc/01-ioc-java

你可能感兴趣的:(b04-2 spring容器相关java知识-反射(精通Spring 4.x))