动态代理
一、概述
- 动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(如代理工厂类)在程序运行时由JVM根据反射机制与动态生成字节码动态生成的。代理对象的代理关系在程序运行时才确立。
- 所谓的反射就是在加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,调用方法,获取成员变量等等,通过这个技术我们就可以在运行时加载、探知、使用编译期间完全未知的类。那么jdk的动态代理就是在程序运行的过程中,动态的将我们维护的检查性的代码,放在正常的业务代码之前,那么怎么调用我正常的业务代码呢,因为业务可能有很多种,也就是说可能会有不同的类,但是都要执行相同的检查性代码,如我们要取钱,或者是修改身份证,都必须验证通过才可以,这显然是两个类,一个是Money,一个是ID,所以我们在调用正常业务代码的时候,其实不知道我们调用的是谁的代码,这时候就用到反射,通过反射,动态的识别类型,然后再调用方法,如我们要取钱,那么传入的应该是一个Money的实例,通过这个实例的getDeclaredMethod,或者是getMethod就可以,获取对应实例的方法,然后即可动态调用需要执行方法,只要在用反射前,加入我们验证的代码即可,这时反射在动态代理中的应用。
- 而动态生成字节码是能够在运行时操作字节码的一种技术,在编译期不能决定要生成字节码的类型,也就是没有对应的java文件,所以就不能生成class文件。也就是说动态代理不会明确实现某一个类的代理类,是针对所有业务类的一个公用的类,由于在编译期不能决定生成哪个业务类的代理类所以就不能生成字节码,当在运行的时候,看我们传入的实例是什么类的,生成对应类的代理类,因为这时候要确定生成一个代理类 ,如果没有字节码文件,那么该类就不会加载,更加不会执行,所以动态代理技术,会把字节码文件动态的拼接出来,形成一个class文件,这就是动态生成字节码的文件。
二、jdk动态代理
- InvocationHandler 是个接口,其具体介绍如下:
实现了 InvocationHandler 接口的类用于加强目标类的主业务逻辑。这个接口中有一个方法 invoke(),具体加强的代码逻辑就是定义在该方法中的。程序调用主业务逻辑时,会自动调用 invoke()方法,也就是说无论调用代理对象上的哪一个方法,其实都是在调用InvocationHandler的invoke()方法。 - InvocationHandler的invoke()方法的介绍如下:
- public Object invoke ( Object proxy, Method method, Object[] args)
- proxy:代表生成的代理对象,也就是Proxy.newProxyInstance()方法,返回的对象
- method:代表目标方法,表示当前被调用方法的反射对象
- args:代表目标方法的参数
- 由于该方法是由代理对象自动调用的,所以这三个参数的值不用程序员给出。第二个参数为 Method 类对象,该类有一个方法也叫 invoke(),可以调用目标类的目标方法。这两个 invoke()方法,虽然同名,但无关。最后要说的是invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然如果方法没有返回值,所以invoke()返回的就必须是null了。
- invoke()方法里面的invoke()方法介绍:
- public Object invoke ( Object obj, Object… args)
- obj:表示目标对象
- args:表示目标方法参数,就是其上一层 invoke 方法的第三个参数
- 该方法的作用是:调用执行 obj 对象所属类的方法,这个方法由其调用者 Method 对象确定。在代码中,一般的写法为method.invoke(target, args);其中,method 为上一层 invoke 方法的第二个参数。这样,即可调用了目标类的目标方法。
三、CGLIB动态代理
- 导入CGLIB的jar包:cglib-full.jar
- 定义目标类,注意不用实现任何接口
- 创建代理类的工厂。该类要实现MethodInterceptor接口。该类中完成三样工作:
- 声明目标类的成员变量,并创建以目标类对象为参数的构造器。用于接收目标对象。
- 定义代理的生成方法,用于创建代理类对象。方法名是任意的。代理对象即目标类的子类。
- 定义回调接口方法,对目标类的增强在这里完成
-intercept方法中各个参数的意义
* proxy:代理对象
* method:代理对象的方法,即增强过的业务方法
* args[ ] :方法参数
* methodProxy:代理对象方法的代理对象
- 声明目标类的成员变量,并创建以目标类对象为参数的构造器。用于接收目标对象。
- 创建测试类