接上文:4.2大白话讲解-----JDK动态代理(Dynamic Proxy)
百度百科:Cglib是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
Cglib动态代理,是JDK静态代理,JDK动态代理之后的另一种代理方式。通过名称我们可以发现,JDK静态代理和JDK动态代理都是由Java官方提供的代理方式;而Cglib动态代理则是第三方的开源项目。在使用时,我们需要引入第三方jar包来实现
虽然JDK动态代理解决了JDK静态代理的一些缺点,但是JDK动态代理也还是存在一些缺点的。诸如:JDK动态代理必须要求代理类实现某个接口类,照这种情况,那么我们普通的类就无法使用动态代理了。按照JDK动态代理实现方法来看,是这么一回事,这明显就是JDK动态代理的缺点,所以Cglib动态代理也就应运而生了
因为JDK的动态代理,是通过接口来进行强制转换的。通过代理生成的代理对象,可以强制转换为接口类型,就可以直接调用该类的相关方法来完成一系列的操作
①相比JDK动态代理,我们可以省去声明接口类的这个步骤,可以解除JDK动态代理的限制
②性能方面:JDK低版本的情况下,Cglib动态代理性能明显高于JDK动态代理,有说快10倍以上,可能出现在更低的JDK版本上吧。随着JDK版本的升级,JDK7以后,JDK动态代理也做出了很大的优化,性能好像已经超出了Cglib动态代理。有网友测试:在运行次数较少(1,000,000)的情况下,jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50,000,000), 动态代理比 cglib 快了接近1倍。(数据来自网络,我没有做测试)
Cglib的动态代理,就是子类继承父类。即:通过生成一个被代理对象的子类,然后重写父类的方法生成以后的对象,所以目标类和方法不能声明为final类型。可以强制转化为被代理对象(也就是自己写的类) 。即:将子类的引用赋值给父类
①因为是三方开源项目,我们需要先引入相应Jar包,此处通过Maven导入依赖
cglib
cglib
3.2.12
②先来一个被代理类对象,此处用到的Student被代理类同前文中JDK动态代理保持不变(Cglib动态代理,Person接口不需要了)
/**
* 被代理类对象
*/
public class Student {
public void findHouse(){
System.out.println("被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习");
}
}
③来声明一个代理类对象CglibProxy
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//使用Cglib,需要实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
//getInstance方法,同JDK动态代理、静态代理一样,都是为了获取到被代理对象的引用
public Object getInstance(Class clazz){
//Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
Enhancer enhancer = new Enhancer();
//把父类设置为谁?这一步就是告诉Cglib,生成的子类要继承哪个类
enhancer.setSuperclass(clazz);
//设置回调,使生成的代理类对象在每调用被代理类方法时,都去调用下面的intercept方法
enhancer.setCallback(this);
//enhancer.create()的工作:1.生成源代码 2.编译成class文件 3.加载到JVM中,生成并返回被代理对象......等等
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("我是中介,我代理的用户需要租一间房屋,如下是用户的一些需求");
System.out.println("-----------------------------------------------------");
//这里有个疑问:代理是需要获取到被代理对象的引用,但是Cglib好像并没有获取被代理对象的引用。那怎么还能用代理呢?
//其实这里这个object的引用,是由Cglib帮我们new出来的
//Cglib new出来的对象,是被代理对象的子类(继承了我们自己写的那个被代理类)
//面向对象编程(OOP)中,在new 子类之前,实际上是默认调用了super()方法的
//也就是说,new了子类的同时,也必须先new出来父类。这就相当于是间接的持有了我们父类的引用
//实际上就是:子类重写了父类的所有方法
//我们改变子类对象的某些属性,是可以间接的操作父类的属性的
//是这样子的,代理便可以通过new子类的形式,间接获取到我们被代理对象的引用了
methodProxy.invokeSuper(object,args);
System.out.println("-----------------------------------------------------");
System.out.println("我是中介,如上就是我代理用户的需求");
return null;
}
}
对代码进行的一些简单解释,请看代码中备注
④来一个测试类
/**
* 测试类
*/
public class test {
public static void main(String[] args) {
//生成的代理类的class文件到指定路径
//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定要保存class文件的路径");
Student student = (Student)new CglibProxy().getInstance(Student.class);
System.out.println(student.getClass());
student.findHouse();
}
}
⑤代码运行结果
我是中介,我代理的用户需要租一间房屋,如下是用户的一些需求
-----------------------------------------------------
被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习
-----------------------------------------------------
我是中介,如上就是我代理用户的需求
//在测试类中,添加如下代码,则会将class文件生成到指定路径
//如若需要查看class文件,可以通过反编译工具来查看
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定要保存class文件的路径");
①JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象(方式不同:一个是实现,一个是继承)
②JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
③JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
至此,JDK静态代理,JDK动态代理,Cglib动态代理到此就告一段落了。
附链接:4.大白话讲解-----代理模式(Proxy Pattern)
4.1大白话讲解-----JDK静态代理(Static Proxy)
4.2大白话讲解-----JDK动态代理(Dynamic Proxy)