JDK动态代理和cglib代理详解
- JDK动态代理
先做一下简单的描述,通过代理之后返回的对象已并非原类所new出来的对象,而是代理对象。JDK的动态代理是基于接口的,也就是说,被代理类必须实现一个或多个接口。主要原因是JDK的代理原理是创建一个与被代理类同等级别(具有同样的继承或实现体系)的类,这里称之为代理类。那么该代理类就具备了被代理类同样的方法,这里同样的方法指的是接口中的方法,由被代理类自己定义的方法将不会被代理。那么问题来了,被代理类中对接口方法的实现又如何被代理类知晓呢?因为在创建代理类的时候还继承了Proxy类。该类中有一个InvocationHandler属性,该属性会持有被代理类的对象,由此,相当于代理对象就持有了被代理对象的引用。因此在调用方法时,会调用代理对象的方法,然后通过InvocationHandler的invoke方法反射调用该代理对象持有的被代理对象的方法。上代码!!!
- interface People {
- public void sayHi();
- }
- class ChinesePeople implements People {
- public ChinesePeople(){}
- public void sayHi() {
- System.out.println("你好!");
- }
- }
- class ProxyFactory implements InvocationHandler {
- private Object targetObject;
- public Object createTargetObject(Object targetObject){
- this.targetObject = targetObject;
- return Proxy.newProxyInstance(this.targetObject.getClass()
- .getClassLoader(),
- this.targetObject.getClass().getInterfaces(), this);
- }
- public Object invoke(Object arg0, Method method, Object[] args)
- throws Throwable {
- Object result = null;
- result = method.invoke(targetObject, args);
- return result;
- }
- }
- 35 public class Tests {
- public static void main(String[] args){
- ProxyFactory pf = new ProxyFactory();
- People p = (People)pf.createTargetObject(new ChinesePeople());
- p.sayHi();
- }
- }
以上就是动态代理的一个简单实现,主要是在15行以后比较重要。createTargetObject方法的参数就是被代理的对象。Proxy.newProxyInstance方法就是通过被代理对象来创建代理对象。在这里debug会发现返回对象的结构和被代理对象的结构不同,当然对应的引用自然不一样。然后在到39行处,此处接收对象时用的接口,并使用了强转型。这就说明了代理类和接口之间的关系。而如果将这行代码修改为用被代理类来接收(ChinesePeople p = (ChinesePeople)pf.createTargetObject(new ChinesePeople());)运行时会抛出类型转换异常。这就解释了生成的代理类和被代理类关系(同等级别)。也解释了为什么JDK代理基于接口了。
然后,大家就知道,在ProxyFactory中就可以对被代理方法做一些处理了。比如:
- public Object invoke(Object arg0, Method method, Object[] args)
- throws Throwable {
- System.out.print("xxx:");
- Object result = null;
- result = method.invoke(targetObject, args);
- System.out.print(",This is proxy");
- return result;
- }
当然,还可以做其他的很多的操作比如对Object的方法不做任何处理,等等。至于如何生成代理类的class,可以根据Proxy.newProxyInstance()详细去追一下源码,下面贴一个代理类的片段:
- public final class $Proxy0 extends Proxy implements People {
- //变量,都是private static Method XXX
- private static Method m3;
- private static Method m1;
- private static Method m0;
- private static Method m2;
- //代理类的构造函数,参数是InvocationHandler实例,
- // Proxy.newInstance方法就是通过这个构造函数来创建代理实例的
- public $Proxy0(InvocationHandler var1) throws Exception{
- super(var1);
- }
- //接口代理方法,在这里InvocationHandler.invoke()来实现对方法的调用
- public final void sayHi(){
- try {
- super.h.invoke(this, m3, (Object[]) null);
- } catch (RuntimeException var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- }
- cglib代理
cglib代理可以对没有接口的类进行代理,它的原理是生成一个被代理类的子类,以代理该类所有的方法。但是,不能对final类以及final方法进行代理。下面看看代码
- public class Test {
- public static void main(String[] args){
- new Test().testProxy();
- }
- public void testProxy() {
- Enhancer en = new Enhancer(); //创建CGLIB增强类
- en.setSuperclass(Dog.class);
- en.setCallback(new MethodInterceptor() {
- public Object intercept(Object target, Method method,
- Object[] args, MethodProxy proxy) throws Throwable {
- System.out.println("before proxy");
- Object o = proxy.invokeSuper(target,args);
- System.out.println("after proxy");
- return o;
- }
- });
- Dog dogProxy = (Dog)en.create();
- dogProxy.eat();
- }
- }
- class Dog{
- public void eat() {
- System.out.println(this.getClass());
- System.out.println("eating");
- }
- }
以上就实现了对没有实现接口的类的代理,并没有通过反射机制来调用,并且完全是通过代理类来调用方法。控制台打印结果:
before proxy
class test.Dog$$EnhancerByCGLIB$$19bdc068
eating
after proxy
在cglib中同样可以实现反射调用和对实现接口类的代理,这种情况下都必须持有被代理的对象引用,首先先看看反射实现
- public class Test {
- public static void main(String[] args){
- new Test().testProxy2();
- }
- public void testProxy2() {
- Dog dog = new Dog(); //创建被代理对象
- Enhancer en = new Enhancer(); //创建CGLIB增强类
- en.setSuperclass(Dog.class);
- en.setCallback(new MethodInterceptor() {
- public Object intercept(Object target, Method method,
- Object[] args, MethodProxy proxy) throws Throwable {
- System.out.println("before proxy");
- Object o = method.invoke(dog, args);
- System.out.println("after proxy");
- return o;
- }
- });
- Dog dogProxy = (Dog)en.create();
- dogProxy.eat();
- }
- }
- class Dog{
- public void eat() {
- System.out.println("eating");
- }
- }
看看打印结果:method.invoke(dog,args)也可以用proxy.invoke(dog,args);
before proxy
class test.Dog
eating
after proxy
下面再看看实现接口后的情况,如果suppserClass是被代理类的父接口或父类的话,则对象必须要用接口或父类来接收,否则会报错。
- public class Test {
- public static void main(String[] args){
- new Test().testProxy();
- }
- public void testProxy() {
- Dog dog = new Dog();
- Enhancer en = new Enhancer(); //创建CGLIB增强类
- en.setSuperclass(Animal.class);
- en.setCallback(new MethodInterceptor() {
- public Object intercept(Object target, Method method,
- Object[] args, MethodProxy proxy) throws Throwable {
- System.out.println("before proxy");
- // Object o = method.invoke(dog,args);
- Object o = proxy.invoke(dog,args);
- System.out.println("after proxy");
- return o;
- }
- });
- // Dog dogProxy = (Dog)en.create();//java.lang.ClassCastException
- Animal dogProxy = (Animal)en.create();
- dogProxy.eat();
- }
- }
- class Dog implements Animal{
- public void eat() {
- System.out.println(this.getClass());
- System.out.println("eating");
- }
- }
- interface Animal{
- public void eat();
- }
看看打印结果:
before proxy
class test.Dog
eating
after proxy
下面也看看cglib生成的class文件的片段。如果methodProxy.invoke()方法的参数是代理对象,则会出现死循环,所以要正常使用invoke()方法,这必须依赖被代理对象。不管是invoke方法还是invokeSuper,都与FastClass有关。
- //methodProxy.invokeSuper会调用
- final void CGLIB$sayHi$0() {
- super.sayHi();
- }
- //methodProxy.invoke会调用
- public final void sayHi() {
- MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
- if(this.CGLIB$CALLBACK_0 == null) {
- CGLIB$BIND_CALLBACKS(this);
- var10000 = this.CGLIB$CALLBACK_0;
- }
- if(var10000 != null) {
- //调用自己实现的拦截器
- var10000.intercept(this, CGLIB$sayHi$0$Method, CGLIB$emptyArgs, CGLIB$sayHi$0$Proxy);
- } else {
- super.sayHi();
- }
- }
- 总结
总结一下两则区别:
- JDK代理是基于接口的代理,而cglib的代理是创建类的子类,可以代理没有实现接口的类。可以理解为一个为横向一个为竖向;
- JDK代理是通过反射调用方法,依赖被代理对象。cglib通过FastClass机制调用,可以不依赖代理对象;
- JDK是通过JNI直接生成代理class,而cglib通过ASM来生成代理class
在cglib的代理中还涉及到了FastClass这个类。这个类的处理现在还没有搞懂,等下次在总结。总的来说,对这两种代理的原理有了详细了解,同事也明白了两种之间的区别。以上来自个人学习总结,不保证全面和完全正确。如有不对之处,请海涵。同时欢迎指正。
JDK动态代理和cglib代理详解的更多相关文章
- 设计模式---JDK动态代理和CGLIB代理
Cglig代理设计模式 /*测试类*/ package cglibProxy; import org.junit.Test; public class TestCglib { @Test public ...
- JDK动态代理和 CGLIB 代理
JDK动态代理和 CGLIB 代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期期间创建一个接口的实现类来完成对目标对象的代理. 代码示例 接口 public interface ...
- JDK动态代理和CGLIB代理的区别
一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- 动态代理:JDK动态代理和CGLIB代理的区别
代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...
- JDK动态代理和cglib代理
写一个简单的测试用例,Pig实现了Shout接口 public class MyInvocation implements InvocationHandler { Object k; public M ...
- SpringAOP-JDK 动态代理和 CGLIB 代理
在 Spring 中 AOP 代理使用 JDK 动态代理和 CGLIB 代理来实现,默认如果目标对象是接口,则使用 JDK 动态代理,否则使用 CGLIB 来生成代理类. 1.JDK 动态代理 那么接 ...
- 静态代理、动态代理和cglib代理
转:https://www.cnblogs.com/cenyu/p/6289209.html 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处 ...
- java的静态代理、jdk动态代理和cglib动态代理
Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...
随机推荐
- [android] 分析setting源代码获取SD卡大小
保存文件到sd卡需要判断sd卡的大小,通过查看android系统的自带应用的源代码,得到方法,sdk下面的source是sdk的源代码,包含的是android.Jar下面的所有class的源代码.在a ...
- C++中的stack类、QT中的QStack类
C++中的stack 实现一种先进后出的数据结构,是一个模板类. 头文件 #include<stack> 用法(以int型为例): stack <int> s; //定义一个i ...
- 程序猿制造Bug的根本原因竟然是....
传说中: 「杀一个程序猿不需要用枪,改三次需求就可以了.」 而且, 「这竟然也是程序猿制造Bug的根本原因....」 ↓↓↓↓↓↓↓ #/原始需求/# 你去饭店,坐下来. “服务员,给我来份宫保鸡 ...
- 【转】AtomicReference与volatile的区别
来源:AtomicReference与volatile的区别 AtomicReference与volatile的在可见性上的意义是一致的. volatile不能保证原子性,AutomicReferen ...
- js节点的类型
1. dom>documentElement>body>tagname 2.我们常用的节点标签. 元素节点(标签) 文本节点 属性节点(标签里的属性) 3.document有个属性n ...
- react学习(三)之生命周期/refs/受控组件 篇
挂载/卸载 //在类组件中 class Clock extends React.Component { constructor(props) { super(props); this.state = ...
- 洛谷P4593 [TJOI2018]教科书般的亵渎(拉格朗日插值)
题意 题目链接 Sol 打出暴力不难发现时间复杂度的瓶颈在于求\(\sum_{i = 1}^n i^k\) 老祖宗告诉我们,这东西是个\(k\)次多项式,插一插就行了 上面的是\(O(Tk^2)\)的 ...
- CSS的继承和使用方式
CSS的继承 css的继承指的是当标签具有嵌套关系时,内部标签自动拥有外部标签的不冲突的样式的性质. 在Css中有些属性不允许继承,例如,border属性没有继承性.多边框类的属性都没有继承 ,例如, ...
- ubuntu12.0.4开启root用户登陆
1.命令:sudo passwd root 为root分配密码,按提示进行设置就好. 2.打开终端,输入以下命令: sudo -s 进入root账户下: cd /etc/lightdm g ...
- g4e基础篇#5 创建分支和保存代码
章节目录 前言 1. 基础篇: 为什么要使用版本控制系统 Git 分布式版本控制系统的优势 Git 安装和设置 了解Git存储库(Repo) 起步 1 – 创建分支和保存代码 起步 2 – 了解Git ...