通俗易懂讲反射

可进入本人语雀文档看,格式更清晰明了哦
https://www.yuque.com/docs/share/3c013ec6-6c35-4854-aaf6-ff9a6e8a6af2?# 《通俗易懂讲反射》

文章架构

img

反射是个啥

反射就是在运行期,可以获取任意一个对象的属性和方法,可以调用任意一个对象的方法和属性。是一种动态获取对象信息以及动态调用对象的方法。最常见的场景就是在动态代理。而动态代理应用最广的地方就是各种框架,比如: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 对象所有的方法信息

img

代理

既然反射应用最广的地方就是动态代理,那么只介绍如何在运行中获取一个类的结构就有点说不过去了。好歹也得介绍下动态代理吧。

动态代理

动态代理一共需要三个东西:

  • 接口,以下的代码是 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() 方法

文章为本人学习过程中的一些个人见解,漏洞是必不可少的,希望各位大佬多多指教,帮忙修复修复漏洞!

posted @ 2021-10-23 14:59  三木同学  阅读(349)  评论(0编辑  收藏  举报