为什么要用动态代理? 因为静态代理需要额外编写代理类,对于每一个要代理的对象,都要书写一个额外的代理类。
使用代理的原因? 有些类是不能够直接访问的或者有些访问要经过特殊处理。
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)来调用被代理类的方法。