Java反射机制以及动态代理

时间:2022-12-24 17:28:55

Java反射机制以及动态代理

Java反射机制

含义与功能

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods

Class类

认识Class类:

Class类是一个比较特殊的类,它是反射机制的基础 ,对于一个字节码文件.class,虽然表面上我们对该字节码文件一无所知,但该文件本身却记录了许多信息。Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。所以要想完成反射操作,就必须首先认识Class类。

在Java中程序获得Class对象有如下3种方式:

  • 使用Class类的静态方法forName(String className),其中参数className表示所需类的全名。例如:Class example = Class.forName("top.changtong1819.java.test")

    看着挺眼熟,这不就是学JDBC时连接数据库时的写法吗?Class.forName(xxxx)

  • 用类名调用该类的class属性来获得该类对应的Class对象,即“类名.class”。

    例如:Class example = test.class

  • 用对象调用getClass()方法来获得该类对应的Class对象,即“对象.getClass()”。

    例如:Class example = new Test("haha").getClass()

通过类的class属性获得该类所对应的Class对象,会使代码更安全,程序性能更好,因此大部分情况下建议使用第二种方式。使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。

创建实例对象:

创建运行时类的对象,使用newInstance(),实际上就是调用运行时指定类的无参构造方法。

 public void test() throws Exception{
Class example =Class.forName("top.changtong1819.Person");
Person person = (Person)example.newInstance();
}

这里单单使用无参构造自然是不行的,满足不了实际使用需求,因此我们还需要想办法创建带参构造,这就是下面要讲的构造器的使用了。

类的反射

获取成员方法:一个成员方法就是一个Method对象

  • getMethods()返回该类继承以及自身声明的所有public的方法数组
  • getDeclaredMethods()返回该类自身声明的所有public的方法数组,不包括继承而来
//用invoke(Object, Object...)可以调用该方法
//带参的public方法:第一个参数是方法名,后面的可变参数列表是参数类型的Class类型
Method test = example.getMethod("test",String.class);
//调用获得的方法,调用时候传递参数
test.invoke(person,"hehe");

获取成员变量:一个成员变量就是一个Fields对象

  • getFields()获取所有的public的成员变量信息,包括继承的。
  • getDeclaredFields()获取该类自己声明的成员变量信息,public,private等

获取Java语言修饰符的int返回值:可调用Modifier.toString()获取修饰符的字符串形式:

  • getModifiers()以整数形式返回由此对象表示的字段的 Java 语言修饰符。

获取注释:

  • getAnnotations()返回此元素上存在的所有注释。
  • getDeclaredAnnotations()返回直接存在于此元素上的所有注释。

获取构造函数Constructor对象

  • getConstructors()返回所有public构造方法
  • getDeclaredConstructors() 返回类的所有构造方法
//Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
Person person = (Person) constructor.newInstance("p", "person");

动态代理

动态代理是指通过代理类来调用它对象的方法,并且是在程序运行使其根据需要创建目标类型的代理对象。它只提供一个代理类,我们只需要在运行时候动态传递给需要他代理的对象就可以完成对不同接口的服务了。

在Java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。

InvocationHandler接口:

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:  指代我们所代理的那个真实对象
method:  指代的是我们所要调用真实对象的某个方法的Method对象
args:  指代的是调用真实对象某个方法时接受的参数

Proxy类

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

动态代理示例:

//定义一个接口
public interface Person{
void say();
void eat();
} //定义该接口的实现类
public class Man implements Person{
void say(){
System.out.println("多喝岩浆");
}
void eat(){
System.out.println("人在家中坐,狗粮天上来");
}
} //定义一个动态代理
public class SingleProxy implements InvocationHandler{
//先声明我们要代理的真实对象
private Object person;
//构造方法,给真实对象赋值
public SingleProxy(Object person){
this.person = person;
}
public Object invoke(Object person, Method method, Object[] args) throws Throwable{
//在代理真实对象前,我们可以添加操作进行增强
System.out.println("咱是单身狗");
//调用真实对象的方法
method.invoke(person,args);
//在代理对象之后,依旧可以增强操作
System.out.println("省钱又省心-->别问,问就是等相亲");
return null;
}
} //主类
public class programmer{
public static void main(String [] args){
//要代理的对象
Man man = new Man();
//将真实对象传入代理类中去
InvocationHandler handler = new SinglerProxy(man);
/*
* 通过Proxy类的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数man.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Person person = (Person)Proxy.newProxyInstance(handler.getClass().getClassLoader(), man.getClass().getInterfaces(), handler);
person.eat();
person.say();
//这样,动态代理就算完成了
}
}