Java 反射和动态代理
1. 类加载机制
1.1 类加载过程
大体来说,可以分为三个阶段:加载 $\rightarrow$ 链接 $\rightarrow$ 初始化。具体过程如图:1
类加载时机:
1.创建类的实例,也就是new一个对象
2.访问某个类或接口的静态变量,或者对该静态变量赋值
3.调用类的静态方法
4.反射
5.初始化一个类的子类(会首先初始化子类的父类)
6.虚拟机启动时标明的启动类,即文件名和类名相同的那个类
1.2 加载
将class
字节码文件内容加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class
对象,作为方法区中类数据(Class Metadata)的返回入口。
Class
对象代表啥:
每当一个类加载到内存中后,这个类便成为运行时类,虚拟机会在堆区创建一个有关这个类的Class
对象。
1.1 类加载过程
1.3 类加载器
Bootstrap Class loader
Extension Class loader
Application Class loader
双亲委托模型:
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。
为什么采用这种模式:
- 避免类的重复加载
- 保证安全, Java中定义的核心类不会被随意替换
2. 反射
反射到底是干啥的?答:不用new也可以获取到一个对象的实例。可以在运行时构造任意一个类的对象,可以在运行时处理注解、获取泛型信息等。
反射相关的api
在java.lang.reflect
包下。以下是通过反射调用show
方法的代码清单。
1 | package com.nefu.reflect; |
3. 动态代理
代理类可以增强被代理类对象方法。
3.1 静态代理
1 | public class Test01 { |
以上代码可以看出,当再有一个类实现ClothFactory
接口,我们得继续编写一个对应的代理类进行增强处理。静态代理在编译期就确定了代理对象。
3.2 动态代理
在Java中,动态代理实现有JDK
自带的动态代理,CGLib
动态代理。 通过动态代理,可以无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。
两种动态代理的最大的区别是:JDK
动态代理要求被代理对象必须基于接口来实现。动态代理类和被代理类必须实现同一个接口。动态代理只能对接口中声明的方法进行代理。对那些没有实现接口的bean。JDK
动态代理无法代理。而CGLib
通过继承被代理类的方式实现代理。
在JDK
动态代理中,主要调用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。依然以静态代理中的ClothFactory
为例,编写动态代理。
1 | import java.lang.reflect.InvocationHandler; |
可以看出,上述代码中并没有显示的编写代理类,而是调用了Proxy.newProxyInstance
方法来动态创建代理类。