通俗易懂讲反射
可进入本人语雀文档看,格式更清晰明了哦
https://www.yuque.com/docs/share/3c013ec6-6c35-4854-aaf6-ff9a6e8a6af2?# 《通俗易懂讲反射》
文章架构
反射是个啥
反射就是在运行期,可以获取任意一个对象的属性和方法,可以调用任意一个对象的方法和属性。是一种动态获取对象信息以及动态调用对象的方法。最常见的场景就是在动态代理。而动态代理应用最广的地方就是各种框架,比如:Spring
Class 对象
了解反射必定离不开 Class 对象。
爪洼国的人都知道,代码编写完后是要编译成 .class 文件的。这里的一个个 .class 文件,被虚拟机加载到内存后就是以 Class 对象存在,一个类只能有一个 .class 文件,因此,一个类也就对应一个 Class 对象,Class 对象是类的描述信息,比如:字段、方法、注释等等等等,只要你想得到的类结构,他在 Class 对象内都有体现,并且都能通过 Class 对象获取到。
如何获取 Class 对象引用
有以下两种方式获取:
- 使用 Class 类的一个静态方法 forName() 获取:Class<?> clazz = Class.forName("className");
- 通过类字面常量获取:Class
= String.class;
Class 类常用方法
这里偷个懒,进入 class 文件,通过 idea alt + 7 快捷键就可以展示类拥有的字段、方法。
通过方法名其实就大概能猜出这个方法是干嘛的了。
比如:
这里的例子可能有点绕,就随便拿一个类来举例:比如 String 类。
String 类有一个描述其结构的 Class 对象,下面用 Class
-
newInstance():通过 Class
对象实例化一个 String 对象。 -
getName():获取 String 对象的类名
-
getInterfaces():获取 String 对象所实现的所有接口信息
-
getMethods():获取 String 对象所有的方法信息
代理
既然反射应用最广的地方就是动态代理,那么只介绍如何在运行中获取一个类的结构就有点说不过去了。好歹也得介绍下动态代理吧。
动态代理
动态代理一共需要三个东西:
-
接口,以下的代码是 Interface 接口
-
实现接口的类,也是要被代理的类,以下的代码是 Clazz 类
-
实现 InvocationHandler 接口的代理类,以下的代码是 ProxyHandler 类。
代理类就是通过现有的字节码,通过增强的方式对要调用的方法进行增强,最后动态生成新的字节码文件,并且生成新的 .class 文件,最后通过反射的方式执行增强的方法。
如下,ProxyHandler 类中的 invoke 方法就是增强后的方法,这里在原来的被代理类执行前和执行后分别打印了两行字符串,实际应用场景可能是事务的开启和提交。
在 DynamicProxyDemo 类的 main 方法里面,通过 Proxy 类的静态方法 newProxyInstance() 生成一个被增强后的代理类,newProxyInstance() 方法需要三个入参
-
类加载器,随便找个已经被加载的类获取其类加载器即可
-
Class 数组,被代理类所实现的接口数组,由此可见,jdk 的动态代理必须实现接口
-
实现 InvocationHandler 接口的代理类对象,必须把被代理的类传入代理类中
获取到增强后的代理类后,将其强转成被代理类对象所实现的接口,然后调用方法,就可以发现,除了原本的实现,增强的逻辑也执行了。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Interface {
void m1();
String m2();
String m3(String a);
}
class Clazz implements Interface {
@Override
public void m1() {
System.out.println("Clazz 的 m1() 方法");
}
@Override
public String m2() {
System.out.println("Clazz 的 m2() 方法");
return "";
}
@Override
public String m3(String a) {
System.out.println("Clazz 的 m3() 方法");
return a;
}
}
class ProxyHandler implements InvocationHandler {
private Object obj;
public ProxyHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("------ method:%s start proxy ------%n", method.getName());
Object invoke = method.invoke(obj, args);
System.out.printf("------ method:%s end proxy ------%n%n", method.getName());
return invoke;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
Interface proxy = (Interface) Proxy.newProxyInstance(DynamicroxyDemo.class.getClassLoader(), new Class[]{Interface.class}, new ProxyHandler(new Clazz()));
proxy.m1();
proxy.m2();
proxy.m3("Clazz 的 m3() 方法");
}
}
运行结果:
------ method:m1 start proxy ------
Clazz 的 m1() 方法
------ method:m1 end proxy ------
------ method:m2 start proxy ------
Clazz 的 m2() 方法
------ method:m2 end proxy ------
------ method:m3 start proxy ------
Clazz 的 m3() 方法
------ method:m3 end proxy ------
静态代理
既然都讲了动态代理,那就来讲讲静态代理吧。
动态代理是在运行时动态生成字节码,然后执行增强方法。
静态代理也需要三个东西:
-
接口
-
实现接口的被代理类
-
实现接口的代理类
其中被代理类实现接口后,实现了自己的代码逻辑。
代理类也实现了相同的接口,然后在对应的方法里调用被代理类的方法就行了,需要增强方法的话在调用方法前或者调用方法后编写增强代码即可,所以代理类需要持有一个被代理类的引用。代码十分简单!看下面的例子。
interface StaticProxyInterface {
void m1();
String m2();
String m3(String a);
}
class StaticProxyClazz implements StaticProxyInterface {
@Override
public void m1() {
System.out.println("Clazz 的 m1() 方法");
}
@Override
public String m2() {
return "Clazz 的 m2() 方法";
}
@Override
public String m3(String a) {
return a;
}
}
class FinalProxyClazz implements StaticProxyInterface {
private StaticProxyInterface obj;
public FinalProxyClazz(StaticProxyInterface obj) {
this.obj = obj;
}
@Override
public void m1() {
System.out.println("------ start proxy ----");
obj.m1();
}
@Override
public String m2() {
System.out.println("------ start proxy ----");
return obj.m2();
}
@Override
public String m3(String a) {
System.out.println("------ start proxy ----");
return obj.m3(a);
}
}
public class StaticProxyDemo {
public static void main(String[] args) {
FinalProxyClazz finalProxyClazz = new FinalProxyClazz( new StaticProxyClazz());
finalProxyClazz.m1();
System.out.println(finalProxyClazz.m2());
System.out.println(finalProxyClazz.m3("Clazz 的 m3() 方法"));
}
}
运行结果:
------ start proxy ----
Clazz 的 m1() 方法
------ start proxy ----
Clazz 的 m2() 方法
------ start proxy ----
Clazz 的 m3() 方法
文章为本人学习过程中的一些个人见解,漏洞是必不可少的,希望各位大佬多多指教,帮忙修复修复漏洞!