java jdk中的动态代理和Cglib中的动态代理的解析

为什么要用动态代理? 因为静态代理需要额外编写代理类,对于每一个要代理的对象,都要书写一个额外的代理类。

使用代理的原因? 有些类是不能够直接访问的或者有些访问要经过特殊处理。

 

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)来调用被代理类的方法。

 

 

 


 

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值