反射
定义
Java 的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
示例
TUser user = new TUser();
user.action("正射");
//构造class
Class clazz = Class.forName("com.tchy.userapi.entity.TUser");
//使用class获取方法
Method method = clazz.getMethod("action", String.class);
method.invoke(clazz.newInstance(), "反射");
//使用class获取类
TUser user1 = (TUser) clazz.getConstructor().newInstance();
user1.action("反射2");
运行结果为:
Connected to the target VM, address: '127.0.0.1:52500', transport: 'socket'
正在做的动作:正射
正在做的动作:反射
正在做的动作:反射2
Disconnected from the target VM, address: '127.0.0.1:52500', transport: 'socket'
Process finished with exit code 0
Class对象
每个类都有一个Class对象,每当编译一个新类就产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中)。比如创建一个Student类,那么,JVM就会创建一个Student对应Class类的Class对象,该Class对象保存了Student类相关的类型信息。
Class类的对象作用是运行时提供或获得某个对象的类型信息
获取Class对象方法
有三种方式
//使用class.forName方法
Class clazz = Class.forName("com.tchy.userapi.entity.TUser");
//使用类的.class获取
Class clazz = TUser.class;
//使用示例对象的getClass()方法
TUser user1 = new TUser();
Class clazz = user1.getClass();
获取到class对象后可以使用class的方法:
- 创造对象:
//使用.newInstance()
TUser user1 = (TUser) clazz.newInstance();
//使用.getConstructor().newInstance()
TUser user2 = (TUser) clazz.getConstructor().newInstance();
- 获取类的构造器:
//获取所有公共的构造方法
clazz.getConstructors();
//获取所有构造方法
clazz.getDeclaredConstructors();
//返回指定参数类型的public的构造器
clazz.getConstructor();
//返回指定参数类型的public的构造器
clazz.getDeclaredConstructor();
- 获取类的成员变量:
//获取类的public类型的属性
clazz.getFields();
//获取所有类的属性
clazz.getDeclaredFields();
//根据变量名,返回一个具有public类型的属性
clazz.getField("action");
//根据变量名,返回一个属性
clazz.getDeclaredField("action");
- 获取类的方法:
//获取类的所有public方法
clazz.getMethods();
//获取类的所有方法
clazz.getDeclaredMethods();
//根据变量名获取类的指定方法
clazz.getMethod("action", String.class);
动态代理
静态代理
就是设计模式中的代理模式 代理模式是一种结构性设计模式,其目的是通过创建一个代理类来控制对另一个对象的访问。代理模式涉及到两个主要角色:
- 主题接口(Subject Interface): 定义了代理类和真实主题类共同实现的接口,这样代理类就可以替代真实主题类。
- 真实主题类(Real Subject Class): 实现了主题接口,是代理类所代表的真实对象。代理类通过调用真实主题类的方法来实现对真实对象的访问。
- 代理类(Proxy Class): 实现了主题接口,包含一个指向真实主题类的引用。代理类可以控制客户端对真实主题类的访问,例如在访问真实主题类的方法前后执行一些操作。
动态代理
解决静态代理每个类都需要创建一个代理类的冗余,则产生了动态代理
在运行状态中,需要代理的地方,根据 Subject 和 RealSubject,动态地创建一个 Proxy,用完之后,就会销毁,这样就可以避免了 Proxy 角色的 class 在系统中冗杂的问题了
Java 动态代理基于经典代理模式,引入了一个 InvocationHandler,InvocationHandler 负责统一管理所有的方法调用。
动态代理步骤:
-
获取 RealSubject 上的所有接口列表;
-
确定要生成的代理类的类名,默认为:
com.sun.proxy.$ProxyXXXX; -
根据需要实现的接口信息,在代码中动态创建 该 Proxy 类的字节码;
-
将对应的字节码转换为对应的 class 对象;
-
创建
InvocationHandler实例 handler,用来处理Proxy所有方法调用; -
Proxy 的 class 对象 以创建的 handler 对象为参数,实例化一个 proxy 对象。
所有动态代理必须要实现 InvocationHandler 这个接口,重写invoke方法; 代码示例:
public class Client {
public static void main(String[] args) {
// 我们要代理的真实对象
Subject realSubject = new RealSubject();
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new InvocationHandlerDemo(realSubject);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
System.out.println(subject.getClass().getName());
subject.hello("World");
String result = subject.bye();
System.out.println("Result is: " + result);
}
}