Java基础—反射与代理(新手向)
第1章 反射与代理
1.1 反射定义
一般情况下,需要一个功能的前提是遇到了某个问题,这里先列举一些问题,然后再通过反射是如何解决了这些问题,来引出反射的定义。
普通开发人员工作中最常见的问题:需要生成代理对象(不清楚代理模式的话,可以简单理解为需要将一个类,在不改变这个类的代码的基础上,要对这个类的功能添加新的逻辑)
解决方式:将需要加强的类,利用反射加载之后,与补充的逻辑进行融合,产生一个新的对象,这个对象就是代理对象,即具备原有类及新逻辑的“增强后的类”(比如 Man 类里面有个 eat() 方法,我们希望执行 eat() 方法前后分别执行洗手、洗碗逻辑,而我们又不能直接去修改eat()方法)
上面是用简洁的方式给出了关于反射的最直白的解释,下面给出较为专业、全面的解释:
反射是什么?
反射(Reflection)是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
通过反射机制,可以在运行时访问 Java 对象的属性,方法,构造方法等
反射的应用场景:
开发通用框架 – 反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象
动态代理 – 在切面编程(AOP)中,需要拦截特定的方法,通常,会选择动态代理方式。这时,就需要反射技术来实现了
注解 – 注解本身仅仅是起到标记作用,它需要利用反射机制,根据注解标记去调用注解解释器,执行行为。如果没有反射机制,注解并不比注释更有用
可扩展性功能 – 应用程序可以通过使用完全限定名称创建可扩展性对象实例来使用外部的用户定义类
1.2 反射与代理中涉及术语
为了理解下一节反射与代理的关系,这里先介绍一下会涉及到的术语:
真实对象(被代理对象):就是最原始的类实例化产生的对象,未经过代理模式对其加工增强,比如上面所说的Man类的对象
代理对象:利用代理模式增强后的对象,比如SuperMan对象
动态代理类:可以理解为代理对象逻辑处理器,也可以理解为“增强”的逻辑所处的位置,需要传入真实对象产生关联的动态代理对象
InvocationHandler 接口:动态代理类需要实现这个接口,并且重写 invoke() 方法,“增强”的逻辑就写在invoke方法里,每个代理类的实例都关联到了一个 Handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用
Proxy:代理类,用以为将动态代理对象传入之后,产生真正代理对象
1.3 反射与代理的关系
如上所述,代理模式的主要作用产生代理对象从而实现增强后的方法,而反射作为 Java 所提供的一个特性,是实现代理模式的基础。换言之,利用反射技术获取和操作Java程序里面的类,从而可以对这些类进行包装及加工,产生出代理对象。
获得实现类对象的代理对象:
2.1 调用 Proxy.newProxyInstance 来获得一个动态的代理对象,其接收三个参数,三个参数所代表的含义分别是:
①一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载
②一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
③ 一个 InvocationHandler 的实现类对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 的实现类对象上
3.获得代理对象的类对象(这里我们可以打印一下代理类的类对象的类名)
4.获得代理类的所有方法(通过暴力反射 getDeclaredMethods()获得)(将所有获得的方法遍历,输出所有的方法名)
5.通过代理对象调用实现类的方法(并赋值),触发我们的重点步骤: InvocationHandler 接口的实现类中的invoked()方法,从而执行实现类的方法(sout输出结果即可)(这一步就是所谓的无侵入式编码规则)
5.1调用 InvocationHandler 接口具体步骤如下:
在触发了实现类的方法后,首先需要在 InvocationHandler 接口中传入三个参数,分别是
① proxy: – 指代我们所代理的那个真实对象
② method: - 指代的是我们所要调用真实对象的某个方法的Method对象()
③ args: - 指代的是调用真实对象某个方法时接受的参数
之后会执行接口独有的invoked()方法,传入两个参数,分别是真实的实现类对象和传入的参数args,最后返回方法。
1.4 JDK动态代理和CGLIB动态代理的区别
代理方式,其实都是通过继承真实对象(被代理对象)的类或者实现其所实现的接口之后,将增强的逻辑补充进去完成的。
JDK动态代理就是通过实现接口完成的,所以当一个类是通过实现接口产生的,就是用JDK动态代理
CGLIB动态代理是通过继承类完成的,所以当一个类没有实现接口,那只能使用JDK动态代理
1.5 Reflection框架
Java里面提供了反射获取类的各个属性及方法的类,但是前提是拿到该类之后才能获取并进行相应的操作,而反射框架Reflections不但能获取classpath下面的类,还能根据特定的注解进行获取。
Reflections通过扫描classpath,索引元数据,并且允许在运行时查询这些元数据。
使用Reflections可以很轻松的获取以下元数据信息:
- 获取某个类型的全部子类
- 只要类型、构造器、方法,字段上带有特定注解,便能获取带有这个注解的全部信息(类型、构造器、方法,字段)
- 获取所有能匹配某个正则表达式的资源
- 获取所有带有特定签名的方法,包括参数,参数注解,返回类型
- 获取所有方法的名字
- 获取代码里所有字段、方法名、构造器的使用
1.6 JsonCat里面对于的动态代理的使用
jsoncat项目链接:https://github.com/Snailclimb/jsoncat
1.7 参考
https://blog.csdn.net/yaomingyang/article/details/80981004
https://zhuanlan.zhihu.com/p/60805342
https://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful