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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.nefu.reflect;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class { public () { } public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Class<?> clazz = Class.forName("com.nefu.reflect.Main"); Object obj = clazz.getDeclaredConstructor().newInstance(); Method show = clazz.getDeclaredMethod("show"); show.setAccessible(true); System.out.println(show.getReturnType()); show.invoke(obj); } public void show() { System.out.println("hello world"); } }
|
3. 动态代理
代理类可以增强被代理类对象方法。
3.1 静态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Test01 { public static void main(String[] args) { NikeClothFactory nikeClothFactory = new NikeClothFactory(); NikeClothFactoryProxy proxy = new NikeClothFactoryProxy(nikeClothFactory); proxy.invoke(); } 大专栏 Java 反射和动态代理class="line">} interface ClothFactory{ void product(); }
class NikeClothFactory implements ClothFactory{ public void product() { System.out.println("Nike 开始生产...."); } }
class NikeClothFactoryProxy{ ClothFactory clothFactory; public NikeClothFactoryProxy(ClothFactory clothFactory) { this.clothFactory = clothFactory; }
public void invoke() { System.out.println("前置处理"); clothFactory.product(); System.out.println("后置处理"); } }
|
以上代码可以看出,当再有一个类实现ClothFactory
接口,我们得继续编写一个对应的代理类进行增强处理。静态代理在编译期就确定了代理对象。
3.2 动态代理
在Java中,动态代理实现有JDK
自带的动态代理,CGLib
动态代理。 通过动态代理,可以无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。
两种动态代理的最大的区别是:JDK
动态代理要求被代理对象必须基于接口来实现。动态代理类和被代理类必须实现同一个接口。动态代理只能对接口中声明的方法进行代理。对那些没有实现接口的bean。JDK
动态代理无法代理。而CGLib
通过继承被代理类的方式实现代理。
在JDK
动态代理中,主要调用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。依然以静态代理中的ClothFactory
为例,编写动态代理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
class PumaClothFactory implements ClothFactory{ public void product() { System.out.println("Puma 源自南美!"); } }
public class Test02 { public static void main(String[] args) { PumaClothFactory obj = new PumaClothFactory(); ClothFactory proxy = (ClothFactory)Proxy .newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new PumaInvocationHandler(obj)); proxy.product(); } }
class PumaInvocationHandler implements InvocationHandler{ ClothFactory obj; public PumaInvocationHandler(ClothFactory clothFactory) { this.obj = clothFactory; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("前置处理"); method.invoke(obj, args); System.out.println("后置处理"); return null; } }
|
可以看出,上述代码中并没有显示的编写代理类,而是调用了Proxy.newProxyInstance
方法来动态创建代理类。