本文共 4306 字,大约阅读时间需要 14 分钟。
为什么要用动态代理? 因为静态代理需要额外编写代理类,对于每一个要代理的对象,都要书写一个额外的代理类。
使用代理的原因? 有些类是不能够直接访问的或者有些访问要经过特殊处理。
1. Java JDK中的动态代理
java jdk中的动态代理,要求被代理的类必须实现一个接口。此外,代理类要实现InvocationHandler接口和该接口中的invoke方法。最后通过绑定被代理类和代理类来实现动态代理。 最后通过一下代码来解释一下。
先定义一个接口:
public interface student { public void learnEnglish(String a); public void learnMath(); }
实现接口的类(被代理类):
public class XiaoMing implements student { @Override public void learnEnglish(String a) { System.out.println("learning English"+a); } @Override public void learnMath() { System.out.println("learning Math"); }}
代理类的实现(实现InvocationHandler接口和实现其接口中的invoke方法)
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class MyProxy implements InvocationHandler { Object obj; //被代理的类 public Object bind(Object obj) //绑定代理与被代理类,返回一个新的代理类(可以理解为被代理类的实例,方便对invoke()回调函数的理解) { this.obj=obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); //可以理解为被代理类的实例 } //调用被代理类方法前进行的处理 public void previous() { System.out.println("previous learning..."); } //调用被代理类之后进行的处理 public void after() { System.out.println("after learning..."); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //invoke方法右三个参数,第一个参数是被代理类对象,method是被代理的方法,args是方法参数 previous(); Object result=method.invoke(this.obj, args); //回调函数,通过反射执行被代理类的方法 after(); return result; }}
测试类的实现:
public class test { public static void main(String[] args) { student sDao=new XiaoMing(); //初始化被代理的类 MyProxy myProxy=new MyProxy(); //初始化代理类 //将代理类和被代理的类进行绑定 sDao=(student)myProxy.bind(sDao); sDao.learnEnglish(" i am here"); }}
JDK的动态代理——根据java的反射机制动态生成
利用反射,获取委托类的类加载器,委托类的所有接口,实例化代理类。通过反射类Proxy以及InvationHandler回调接口实现的。但是动态代理类只能对该类所实现的接口中的方法进行代理。具有一定的局限性,并且反射的效率也不是很高。
Proxy 毕竟是通过反射实现的,必须在效率上付出代价:有实验数据表明,调用反射比一般的函数开销至少要大 10 倍。从程序实现上可以看出,对 proxyclass 的所有方法调用都要通过使用反射的 invoke 方法。因此,对于性能关键的应用,使用 proxy class是需要精心考虑的,以避免反射成为整个应用的瓶颈。
2. Cglib中的动态代理:
CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比反射效率要高。但是需要主要注意的,CGLib不能对声明为final的方法进行代理。因为CGLib原理是动态的生成被代理类的子类。
ASM能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。它是一个普通的 Java 类而不是 proxy类,甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。
在调用目标方法的时候,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
代码如下:
被代理的类(并不需要实现一个接口或者继承一个父类,这就很灵活了)
public class XiaoMing{ public void learnEnglish(String a) { System.out.println("learning English"+a); } public void learnMath() { System.out.println("learning Math"); } }
代理类(需要实现MethodInterceptor接口和其中的intercept方法):
import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; //一个字节码增强类,它可以方便的对你想要处理的类进行扩展。import net.sf.cglib.proxy.MethodInterceptor; //方法拦截器import net.sf.cglib.proxy.MethodProxy; //代理类,可以方便的实现对源对象方法的调用,如invokeSuper()public class CglibProxy implements MethodInterceptor{ private Object originalObject; //被代理类 public Object bind(Object obj) { this.originalObject=obj; Enhancer enhancer=new Enhancer(); //增强类 enhancer.setSuperclass(obj.getClass()); //设置被代理类字节码(obj将被代理类设置成父类,作为产生的代理的父亲传进来的),CGLIB根据字节码生成被代理类的子类 enhancer.setCallback(this); //设置回调函数,即一个方法拦截 return enhancer.create(); //创建代理类 } public void previous() { System.out.println("previous learning..."); } public void after() { System.out.println("after learning..."); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //intercept是一个拦截器,obj指的是被代理类的对象,method是拦截的方法, args是方法参数,proxy拦截器 previous(); proxy.invokeSuper(obj, args); //调用父类方法(父类即是被代理类), 详看enhancer.setSuperclass() after(); return null; }}
测试类的实现:
public class test { public static void main(String[] args) { XiaoMing sDao=new XiaoMing(); //生成被代理的对象 CglibProxy proxy=new CglibProxy(); //代理对象 sDao=(XiaoMing) proxy.bind(sDao); sDao.learnEnglish(" ..."); sDao.learnMath(); }}
4. Cglib动态代理类的使用:
Hibernate主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制。Spring则是利用cglib来实现动态代理。Spring和Hibernate同时支持proxy类动态代理和cglib。Cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类(在cglib中,实际上,代理类是通过继承被代理类来创建的,具体看enhancer.setSuperclass)。
5. JDK和Cglib中动态代理的比较:
(1) JDK中要求被代理的类实现一个接口,而cglib中,普通类就可以做为被代理类,但是被final修饰的类不行,因为cglib是通过生成一个被代理的子类实现动态代理的。
(2) cglib中实现动态代理,你可以理解为就是一个拦截器。而jdk中,就是通过回调函数来实现的,通过反射(invoke)来调用被代理类的方法。
转载地址:http://cckmi.baihongyu.com/