JDK动态代理和CGLIB动态代理

转载自http://www.itzhai.com/java-dong-tai-dai-li-zhi-jdk-dong-tai-dai-li-he-cglib-dong-tai-dai-li-mian-xiang-qie-mian-bian-cheng-aop-yuan-li.html

静态代理

静态代理相对来说比较简单,无非就是聚合+多态:

参考:设计模式笔记 – Proxy 代理模式 (Design Pattern)

动态代理

我们知道,通过使用代理,可以在被代理的类的方法的前后添加一些处理方法,这样就达到了类似AOP的效果。而JDK中提供的动态代理,就是实现AOP的绝好底层技术。

JDK动态代理

JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

例子:Java笔记 – 反射 动态代理

CGLib动态代理

还有一个叫CGLib的动态代理,CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

例子:

首先是两个测试类:

interfaceAnimal{void sound();}classDogimplementsAnimal{publicvoid sound(){System.out.println("wong~");}}

接下来演示CGLib的使用:

publicclassCglibProxyimplementsMethodInterceptor{privateEnhancer enhancer =newEnhancer();publicObject getProxy(Class clazz){// 设置需要创建子类的类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);// 通过字节码技术动态创建子类实例return enhancer.create();}// 拦截父类所有方法的调用@OverridepublicObject intercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{System.out.println("begin...");// 通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.println("end...");returnnull;}publicstaticvoid main(String[] args){CglibProxy proxy =newCglibProxy();Animal dog =(Animal)proxy.getProxy(Dog.class);
        dog.sound();}}

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

JDK动态代理和CGLib的比较

CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。

同样的,Spring的AOP编程中相关的ProxyFactory代理工厂内部就是使用JDK动态代理或CGLib动态代理的,通过动态代理,将增强(advice)应用到目标类中。

Spring中的动态代理的使用

Spring定义了org.springframework.aop.framework.AopProxy接口,并提供了如下两种final类型的实现类:

关于Spring中选择代理类型的判断

如果通过ProxyFactory的setInterfaces(Class[] interfaces)指定针对接口进行代理,ProxyFactory就使用JdkDynamicAopProxy,如果是针对类的代理,则使用Cglib2AopProxy。另外,如果使用ProxyFactory的setOptimize(true)方法,则启动了优化代理方式,这样针对接口的代理也会使用Cglib2AopProxy。

在引介增强中就需要强制指定为Cglib2AopProxy,因为引介增强是一种比较特殊的增强类型,不是在目标周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点事类级别的。而非方法级别的。

JDK动态代理原理浅析

下面来探析下JDK动态代理的原理,看看为什么JDK动态代理的性能会比较低。

动态代理的基本原理为反射 + 多态 + 聚合

首先创建一个接口,和一个需要被代理的类:

interfaceAnimal{void sound();}classDogimplementsAnimal{publicvoid sound(){System.out.println("wong~");}}

接下来是创建一个MyInvocationHandler,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起:

publicinterfaceMyInvocationHandler{publicvoid invoke(Method m,Object... args)throwsException;}

接下来是创建一个MyInvocationHandler的实现类:

package com.itzhai.javanote.proxy;import java.lang.reflect.Method;publicclassAnimalHandlerimplementsMyInvocationHandler{privateObject animal;publicAnimalHandler(Object animal){this.animal = animal;}publicvoid invoke(Method m,Object[] args)throwsException{System.out.println("before");
        m.invoke(animal, args);System.out.println("after");}}

接下里是创建代理类Proxy,里面包含一个newProxy方法,该方法生成需要代理的接口相关的所有方法,通过MyInvocationHandler的invoke方法,调用实际的对象,通过反射机制执行实际的方法,并且需要嵌入的代码也就在invoke方法里面被执行到了:

下面是使用我们创建的动态代理的代码:

package com.itzhai.javanote.proxy;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.JavaFileObject;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;publicclassMyProxy{publicstaticObject newProxyInstance(Class interfaces,MyInvocationHandler handler)throwsException{String r ="\n";Method[] methods = interfaces.getMethods();StringBuffer sb =newStringBuffer("");// 生成需要代理的接口中的所有方法 的代码,通过调用MyInvocationHandler的invoke方法实现真实对象方法的调用for(int i =0;i<methods.length;i++){
            sb.append(" public void "+methods[i].getName()+"() {"+ r +"       try{ "+ r +"       Method md = "+interfaces.getName()+".class.getMethod(\""+methods[i].getName()+"\");"+ r +"       handler.invoke( md,new Object[]{});"+ r +"       }catch(Exception e){e.printStackTrace();}"+ r +"   }"+r + r
            );}// 生成完整的类代码String src ="package com.itzhai.javanote.proxy;"+ r + r +"import java.lang.reflect.*;"+ r + r +"public class Proxy$1 implements "+interfaces.getName()+"{"+ r +"   private com.itzhai.javanote.proxy.MyInvocationHandler handler;"+ r +"   public Proxy$1("+handler.getClass().getName()+" handler){"+ r +"       this.handler = handler;"+ r +"   }"+ r + r +  

            sb.toString()+"}"+r;// 输出Java文件String dir =System.getProperty("user.dir")+"/src/com/itzhai/javanote/proxy/";FileWriter writer =newFileWriter(newFile(dir+"Proxy$1.java"));
        writer.write(src);
        writer.flush();
        writer.close();// 编译动态代理类JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,null,null);Iterable<?extendsJavaFileObject> units = fileMgr.getJavaFileObjects(dir+"Proxy$1.java");Iterable<String> options =Arrays.asList("-d",System.getProperty("user.dir")+"/bin/");CompilationTask t = compiler.getTask(null, fileMgr,null, options,null, units);
        t.call();
        fileMgr.close();// 加载动态代理类,并返回动态代理类的实例
        URL[] urls =new URL[]{new URL("file:/"+dir)};URLClassLoader loader =newURLClassLoader(urls);Class c = loader.loadClass("com.itzhai.javanote.proxy.Proxy$1");Constructor ctr = c.getConstructor(handler.getClass());return ctr.newInstance(handler);}}

分析可以得出,当我们通过MyProxy的newProxyInstance()方法返回代理对象时,实际上是通过传入的接口和MyInvocationHandler生成了一个代理类的字节码,并而代理类中的真实对象的方法是通过反射机制进行调用的(我们在实现invoke方法时都是通过反射的APIMethod的invoke调用具体的方法可知),这也是JDK动态代理在创建代理对象快而运行时却比较慢的原因。

感兴趣的朋友可以去看一下JDK的动态代理实现的源代码(JDK中通过Proxy中的getProxyClass(loader, interfaces)方法生成代理代理对象)。

如果要代理具体的业务类,需要使用CGLib动态代理生成子类了。

静态代理

静态代理相对来说比较简单,无非就是聚合+多态:

参考:设计模式笔记 – Proxy 代理模式 (Design Pattern)

动态代理

我们知道,通过使用代理,可以在被代理的类的方法的前后添加一些处理方法,这样就达到了类似AOP的效果。而JDK中提供的动态代理,就是实现AOP的绝好底层技术。

JDK动态代理

JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

例子:Java笔记 – 反射 动态代理

CGLib动态代理

还有一个叫CGLib的动态代理,CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

例子:

首先是两个测试类:

interfaceAnimal{void sound();}classDogimplementsAnimal{publicvoid sound(){System.out.println("wong~");}}

接下来演示CGLib的使用:

publicclassCglibProxyimplementsMethodInterceptor{privateEnhancer enhancer =newEnhancer();publicObject getProxy(Class clazz){// 设置需要创建子类的类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);// 通过字节码技术动态创建子类实例return enhancer.create();}// 拦截父类所有方法的调用@OverridepublicObject intercept(Object obj,Method method,Object[] args,MethodProxy proxy)throwsThrowable{System.out.println("begin...");// 通过代理类调用父类中的方法Object result = proxy.invokeSuper(obj, args);System.out.println("end...");returnnull;}publicstaticvoid main(String[] args){CglibProxy proxy =newCglibProxy();Animal dog =(Animal)proxy.getProxy(Dog.class);
        dog.sound();}}

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

JDK动态代理和CGLib的比较

CGLib所创建的动态代理对象的性能比JDK所创建的代理对象性能高不少,大概10倍,但CGLib在创建代理对象时所花费的时间却比JDK动态代理多大概8倍,所以对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建新的实例,所以比较适合CGLib动态代理技术,反之则适用于JDK动态代理技术。另外,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final,private等方法进行处理。所以,大家需要根据实际的情况选择使用什么样的代理了。

同样的,Spring的AOP编程中相关的ProxyFactory代理工厂内部就是使用JDK动态代理或CGLib动态代理的,通过动态代理,将增强(advice)应用到目标类中。

Spring中的动态代理的使用

Spring定义了org.springframework.aop.framework.AopProxy接口,并提供了如下两种final类型的实现类:

关于Spring中选择代理类型的判断

如果通过ProxyFactory的setInterfaces(Class[] interfaces)指定针对接口进行代理,ProxyFactory就使用JdkDynamicAopProxy,如果是针对类的代理,则使用Cglib2AopProxy。另外,如果使用ProxyFactory的setOptimize(true)方法,则启动了优化代理方式,这样针对接口的代理也会使用Cglib2AopProxy。

在引介增强中就需要强制指定为Cglib2AopProxy,因为引介增强是一种比较特殊的增强类型,不是在目标周围织入增强,而是为目标类创建新的方法和属性,所以引介增强的连接点事类级别的。而非方法级别的。

JDK动态代理原理浅析

下面来探析下JDK动态代理的原理,看看为什么JDK动态代理的性能会比较低。

动态代理的基本原理为反射 + 多态 + 聚合

首先创建一个接口,和一个需要被代理的类:

interfaceAnimal{void sound();}classDogimplementsAnimal{publicvoid sound(){System.out.println("wong~");}}

接下来是创建一个MyInvocationHandler,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起:

publicinterfaceMyInvocationHandler{publicvoid invoke(Method m,Object... args)throwsException;}

接下来是创建一个MyInvocationHandler的实现类:

package com.itzhai.javanote.proxy;import java.lang.reflect.Method;publicclassAnimalHandlerimplementsMyInvocationHandler{privateObject animal;publicAnimalHandler(Object animal){this.animal = animal;}publicvoid invoke(Method m,Object[] args)throwsException{System.out.println("before");
        m.invoke(animal, args);System.out.println("after");}}

接下里是创建代理类Proxy,里面包含一个newProxy方法,该方法生成需要代理的接口相关的所有方法,通过MyInvocationHandler的invoke方法,调用实际的对象,通过反射机制执行实际的方法,并且需要嵌入的代码也就在invoke方法里面被执行到了:

下面是使用我们创建的动态代理的代码:

package com.itzhai.javanote.proxy;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;import javax.tools.JavaCompiler;import javax.tools.JavaCompiler.CompilationTask;import javax.tools.JavaFileObject;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;publicclassMyProxy{publicstaticObject newProxyInstance(Class interfaces,MyInvocationHandler handler)throwsException{String r ="\n";Method[] methods = interfaces.getMethods();StringBuffer sb =newStringBuffer("");// 生成需要代理的接口中的所有方法 的代码,通过调用MyInvocationHandler的invoke方法实现真实对象方法的调用for(int i =0;i<methods.length;i++){
            sb.append(" public void "+methods[i].getName()+"() {"+ r +"       try{ "+ r +"       Method md = "+interfaces.getName()+".class.getMethod(\""+methods[i].getName()+"\");"+ r +"       handler.invoke( md,new Object[]{});"+ r +"       }catch(Exception e){e.printStackTrace();}"+ r +"   }"+r + r
            );}// 生成完整的类代码String src ="package com.itzhai.javanote.proxy;"+ r + r +"import java.lang.reflect.*;"+ r + r +"public class Proxy$1 implements "+interfaces.getName()+"{"+ r +"   private com.itzhai.javanote.proxy.MyInvocationHandler handler;"+ r +"   public Proxy$1("+handler.getClass().getName()+" handler){"+ r +"       this.handler = handler;"+ r +"   }"+ r + r +  

            sb.toString()+"}"+r;// 输出Java文件String dir =System.getProperty("user.dir")+"/src/com/itzhai/javanote/proxy/";FileWriter writer =newFileWriter(newFile(dir+"Proxy$1.java"));
        writer.write(src);
        writer.flush();
        writer.close();// 编译动态代理类JavaCompiler compiler =ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null,null,null);Iterable<?extendsJavaFileObject> units = fileMgr.getJavaFileObjects(dir+"Proxy$1.java");Iterable<String> options =Arrays.asList("-d",System.getProperty("user.dir")+"/bin/");CompilationTask t = compiler.getTask(null, fileMgr,null, options,null, units);
        t.call();
        fileMgr.close();// 加载动态代理类,并返回动态代理类的实例
        URL[] urls =new URL[]{new URL("file:/"+dir)};URLClassLoader loader =newURLClassLoader(urls);Class c = loader.loadClass("com.itzhai.javanote.proxy.Proxy$1");Constructor ctr = c.getConstructor(handler.getClass());return ctr.newInstance(handler);}}

分析可以得出,当我们通过MyProxy的newProxyInstance()方法返回代理对象时,实际上是通过传入的接口和MyInvocationHandler生成了一个代理类的字节码,并而代理类中的真实对象的方法是通过反射机制进行调用的(我们在实现invoke方法时都是通过反射的APIMethod的invoke调用具体的方法可知),这也是JDK动态代理在创建代理对象快而运行时却比较慢的原因。

感兴趣的朋友可以去看一下JDK的动态代理实现的源代码(JDK中通过Proxy中的getProxyClass(loader, interfaces)方法生成代理代理对象)。

如果要代理具体的业务类,需要使用CGLib动态代理生成子类了。

时间: 2024-10-05 07:17:01

JDK动态代理和CGLIB动态代理的相关文章

深入探索spring技术内幕(六): JDK动态代理和cglib生成代理

[ JDK生成代理 ] JDK中给我们提供了一个Proxy类可以动态的给我们生成代理. 假定我们要做一个权限管理系统, 需要控制用户对某一个方法的访问. 如果user为null, 那么不让用户访问save方法. ① 接口类: PersonService public interface PersonService { public void save(); } ② 实现类: PersonServiceImpl public class PersonServiceImpl implements P

关于JDK动态代理和CGLIB动态代理

1. 代理模式 一句话总结:为其他对象提供一种代理以控制对这个对象的访问.千篇一律的介绍:代理模式是常用的java设计模式,他的特征是代理类与委托类(或目标类)有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工具

spring aop原理 JDK动态代理和CGLIB动态代理

Spring的两大特性是IOC和AOPIOC负责将对象动态的注入到容器,从而达到一种需要谁就注入谁,什么时候需要就什么时候注入的效果.理解spring的ioc也很重要.但是今天主要来和大家讲讲aop.AOP 广泛应用于处理一些具有横切性质的系统级服务,AOP 的出现是对 OOP 的良好补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理.日志.缓存等等. AOP实现的关键在于AOP框架自动创建的AOP代理.AOP代理主要分为静态代理和动态代理, 静态代理的代表为AspectJ:动态代理则

jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

代理模式是一种很常见的模式,关于底层原理网上看到很多的有关的讲解,但看了一些都觉得比较粗略,很多时候把底层代码copy下来也不大讲解,感觉不如自己详细的写上一篇.本文将以非常详细的说明来分析cglib动态代理底层的实现原理,篇幅较长,但是每个核心方法代码中每步都有说明.还请耐心阅读 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency&

JDK动态代理和CGLIB动态代理+源码下载

在上一篇文章-java代理详解讲解实现机制,一种是继承另外一种是组合,而且通过做实现也证明使用组合的方式更加的灵活.之后提到了代理的两种种类,一种是静态代理,另外一种是动态代理.上一篇文件中着重介绍的是静态代理(相对于动态代理很容易理解).这一片文章就接着介绍动态代理. 动态代理实现的最终效果:通过以一个统一的方式实现对任意的接口/类的代理.相比较静态代理而言,我们可以不用再无限制的增加代理类,不用再写许多重复的代码.很符合面向对象设计原则中的"开闭原则":对修改关闭,对扩展开放. 动

【Spring】AOP之JDK动态代理和CGLib动态代理

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 一.JAVA动态代理  1.1 代理模式         代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务. 按照代理的创

Spring 静态代理+JDK动态代理和CGLIB动态代理

代理分为两种:静态代理 动态代理 静态代理:本质上会在硬盘上创建一个真正的物理类 动态代理:本质上是在内存中构建出一个类. 如果多个类需要进行方法增强,静态代理则需要创建多个物理类,占用磁盘空间.而动态代理则是在内存中创建,不会对磁盘进行影响. 静态代理和JDK动态代理需要有接口. CGLIB动态代理无需有接口的情况下进行代理.实质:内存中构建出了目标类型的子类. JDK动态代理是JDK提供的,CGLIB动态代理是Spring提供的. 代理模式的三个组件:抽象主题 真实主题 代理主题 静态代理:

jdk动态代理和cglib动态代理的区别

CGLIB代理: CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类. CGLIB(CODE GENERLIZE LIBRARY)代理是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的所有方法,所以该类或方法不能声明称final的. 如果目标对象实现了接口,可以强制使用CGLIB实现代理. 如果目标对象没有实现接口,则默认会采用CGLIB代理: 为什么jdk动态代理必须基于接口 原因如下: 1.生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口

spring源码学习【准备】之jdk动态代理和cglib动态代理的区别和性能

一:区别:---->JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了.--->JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理. 性能:--->jdk的动态代理由于jdk版本的升级,渐渐超越cglib 二:都说 Cglib 创建的动态代理的