likes
comments
collection
share

java反射与动态代理

作者站长头像
站长
· 阅读数 5

前言

本章将围绕以下问题进行分析

  • 什么是反射
  • 反射的使用
  • 什么是动态代理
  • 动态代理的使用

正文

反射

定义

在程序运行时可以动态的修改类或对象的属性和方法(私有、公有和静态等)。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

使用

1、构造Class类

//Class clas = 类名.class;
Class clas = Class.forName("相对路径的类名");

2、获取类的方法

(一)、构造方法
//根据构造方法的参数获取指定的公有构造方法 参数如:String.class,int.class
Constructor singleConstructor1 = clas.getConstructor(XXX.class,....);

//根据构造方法的参数获取指定的构造方法 参数如:String.class,int.class
Constructor singleConstructor2 = clas.getDeclaredConstructor(XXX.class,....);

//获取类的所有公有构造方法
Constructor[] constructors = clas.getConstructors();

//获取类的所有构造方法()
Constructor[] declaredConstructors = clas.getDeclaredConstructors();

可以通过Class类getConstructor(Class<?>... parameterTypes)getConstructors()获取指定类的公有构造方法,getDeclaredConstructor(Class<?>... parameterTypes)getDeclaredConstructors()获取指定类的所有访问域(公有、保护、私有)构造方法

  • getConstructor(Class<?>... parameterTypes)通过传入参数获取类对应的公有构造方法。参数的值是Class类,如:String.class、int.class等;并且传入参数的值和构造方法参数类型必须一一对应,否则会出现NoSuchMethodException异常。
  • getConstructors()获取类的所有公有构造方法
  • getDeclaredConstructor(Class<?>... parameterTypes)通过传入参数获取类对应的访问域(公有、保护、私有)构造方法。参数的值是Class类,如:String.class、int.class等;并且传入参数的值和构造方法参数类型必须一一对应,否则会出现NoSuchMethodException异常。
  • getDeclaredConstructors()获取类中所有访问域(公有、保护、私有)的构造方法
(二)、其他方法
//获取指定类或指定类超类对应的公有方法
Method method1 = clas.getMethod("方法名称",XXX.class,.....);

//获取指定类对应访问域方法(不能获取超类的方法)
Method method2 = clas.getDeclaredMethod("方法名称",XXX.class,.....);

//获取指定类或指定类超类的所有公有方法
Method[] methods = clas.getMethods();

//获取指定类所有访问域方法(不能获取超类的方法)
Method[] declaredMethods = clas.getDeclaredMethods();

可以通过Class类getMethod(String name, Class<?>... parameterTypes)getMethods()获取指定类或其超类的公有方法getDeclaredMethod(String name, Class<?>... parameterTypes)getDeclaredMethods()获取指定类的所有访问域(公有、保护、私有)方法,但不能获取到其超类的方法

  • getMethod(String name, Class<?>... parameterTypes)通过传入方法名和方法参数获取类或其超类对应的公有方法。参数的值是Class类,如:String.class、int.class等;并且传入参数的值和方法参数类型必须一一对应,否则会出现NoSuchMethodException异常。
  • getMethods()获取类和其超类的所有公有方法
  • getDeclaredMethod(String name, Class<?>... parameterTypes)通过传入方法名和方法参数获取类对应访问域的方法。参数的值是Class类,如:String.class、int.class等;并且传入参数的值和方法参数类型必须一一对应,否则会出现NoSuchMethodException异常。
  • getDeclaredMethods()获取类中所有访问域(公有、保护、私有)的方法

3、获取类的属性

//获取指定类或其超类对应的公有属性
Field field1 = clas.getField("属性名");

//获取指定类对应的所有访问域属性
Field field2 = clas.getDeclaredField("属性名");

//获取指定类或其超类的公有属性
Field[] fields = clas.getFields();

//获取指定类所有的属性
Field[] declaredFields = clas.getDeclaredFields();

可以通过Class类getField(String name)getFields()获取指定类或其超类的公有属性getDeclaredField(String name)getDeclaredFields()获取指定类的所有访问域(公有、保护、私有)属性,但不能获取到其超类的属性

  • getField(String name)通过传入属性名获取类或其超类对应的公有属性。传入的属性名必须在指定类或其超类存在,否则会出现NoSuchFieldException异常。
  • getFields()获取类和其超类的所有公有属性
  • getDeclaredField(String name)通过传入属性名获取类对应的访问域(公有、保护、私有)属性。传入的属性名必须在指定类存在,否则会出现NoSuchFieldException异常。
  • getDeclaredFields()获取类中所有访问域(公有、保护、私有)的属性

4、反射调用类的方法

伪代码(非静态方法)
  1. 构造Class类
  2. 构建Class对象
  3. 获取class对象方法并调用
代码(非静态方法)
try {
    //构造class类
    Class clas = Class.forName("相对路径类名");
    //获取构造方法
    Constructor singleConstructor = clas.getDeclaredConstructor();
    //如果是私有构造方法必须关闭访问检测
    singleConstructor.setAccessible(true);
    //创建对象
    Object instance = singleConstructor.newInstance();
    //调用方法
    Method setColor = clas.getMethod("方法名", xxx.class,....);
    //设置方法参数值
    setColor.invoke(instance, "参数值");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
}

调用对象的方法前,必须要先构建class对象。可以使用Class类的newInstance()或者Class构造方法的newInstance(Object ... initargs)构建Class对象。但使用Class类的newInstance()构建对象时Class类的默认构造方法必须是public的,否则会构建失败。而使用Class构造方法的newInstance(Object ... initargs)构建Class对象时,构造方法可以是任意访问域(public、private、protected),如果是private或者protected访问域,必须关闭访问检测(setAccessible(true)),否则也会构建失败。

伪代码(静态方法 static或static final修饰)
  1. 构造Class类
  2. 获取class类的静态方法并调用
代码(静态方法 static或static final修饰)
try {
     //构造class类
     Class clas = Class.forName("相对路径类名");
//            Method method = clas.getMethod("参数名");
     Method method = clas.getDeclaredMethod("参数名");
     method.setAccessible(true);
     method.invoke(null,"参数值");
     } catch (ClassNotFoundException e) {
       e.printStackTrace();
     } catch (NoSuchMethodException e) {
       e.printStackTrace();
     } catch (IllegalAccessException e) {
       e.printStackTrace();
     } catch (InvocationTargetException e) {
       e.printStackTrace();
     }

5、反射修改类的属性值

伪代码(非静态属性)
  1. 构造Class类
  2. 构建Class对象
  3. 获取Class对象属性并修改属性值
代码(非静态属性)
try {
    //构造class类
    Class clas = Class.forName("相对路径类名");
    //获取构造方法
    Constructor singleConstructor = clas.getDeclaredConstructor();
    //如果是私有构造方法必须关闭访问检测
    singleConstructor.setAccessible(true);
    //创建对象
    Object instance = singleConstructor.newInstance();
    //获取属性
    Field field = clas.getDeclaredField("属性名");
    //关闭访问检测(私有属性必须关闭)
    field.setAccessible(true);
    //修改属性值
    field.set(instance, "属性值");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}
伪代码(静态属性 static修饰)
  1. 构造Class类
  2. 构建Class的静态属性并修改属性值
代码(静态属性 static修饰)
try {
    //构造class类
    Class clas = Class.forName("相对路径类名");
    //   Field field = clas.getField("属性名");
    Field field = clas.getDeclaredField("属性名");
    field.setAccessible(true);
    field.set(null,"属性值");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}
伪代码(静态常量属性 static final修饰)
  1. 构造Class类
  2. 构建Class的静态属性,去除final的影响
  3. 修改属性值
代码(静态常量属性 static final修饰)
try {
    //构造class类
    Class clas = Class.forName("相对路径类名");
    //   Field field = clas.getField("属性名");
    Field field = clas.getDeclaredField("属性名");
    field.setAccessible(true);
    //去除final的影响
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    AccessController.doPrivileged((PrivilegedAction) () -> {
        modifiersField.setAccessible(true);
        return null;
    });
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    //设置属性值
    field.set(null, "属性值");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}

动态代理

定义

使用反射的方式,动态地获取抽象接口的类型,从而获取相关特性进行代理。

使用代码

接口类 proxyService = (接口类) Proxy.newProxyInstance(接口类.getClassLoader(), new Class<?>[]{接口类}, (proxy, method, args1) -> {
    //进行拦截
    ......
    //返回实现类的实现
    return method.invoke("实现类",args);
});
接口类.实现方法();

以上是创建代理模式的公式。在实际开发中,我们可以使用代理类进行拦截实体类的实现或者加入自己的实现逻辑。