博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java jdk中的动态代理和Cglib中的动态代理的解析
阅读量:4212 次
发布时间:2019-05-26

本文共 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/

你可能感兴趣的文章
Oracle Golden Gate 系列五 -- GG 使用配置 说明
查看>>
Oracle Golden Gate 系列六 -- 11gR2 Ora2Ora 单向复制 GG 示例
查看>>
Oracle Golden Gate 系列七 -- 配置 GG Manager process
查看>>
ORA-00600:[32695], [hash aggregation can't be done] 解决方法
查看>>
Oracle SQL中使用正则表达式 执行报ORA-07445 [_intel_fast_memcpy.A()+10] 错误
查看>>
Oracle TABLE ACCESS BY INDEX ROWID 说明
查看>>
ORA-00600 [kmgs_parameter_update_timeout_1], [27072] ORA-27072 解决方法
查看>>
Oracle 11g alert log 新增消息 opiodr aborting process unknown ospid (1951) as a result of ORA-28 说明
查看>>
Linux Context , Interrupts 和 Context Switching 说明
查看>>
《Oracle数据库问题解决方案和故障排除手册》终于发售了
查看>>
Oracle alert log ALTER SYSTEM SET service_names='','SYS$SYS.KUPC$C_...' SCOPE=MEMORY SID='' 说明
查看>>
Oracle latch:library cache 导致 数据库挂起 故障
查看>>
Openfiler 配置 NFS 示例
查看>>
Oracle 11.2.0.1 RAC GRID 无法启动 : Oracle High Availability Services startup failed
查看>>
Oracle 18c 单实例安装手册 详细截图版
查看>>
Oracle Linux 6.1 + Oracle 11.2.0.1 RAC + RAW 安装文档
查看>>
Oracle 11g 新特性 -- Online Patching (Hot Patching 热补丁)说明
查看>>
Oracle 11g 新特性 -- ASM 增强 说明
查看>>
Oracle 11g 新特性 -- Database Replay (重演) 说明
查看>>
Oracle 11g 新特性 -- 自动诊断资料档案库(ADR) 说明
查看>>