Java中jdk代理和cglib代理

代理模式


给某一个对象提供一个代理,并由代理对象控制对原对象的引用。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

在Java中代理模式从实现方式上可以分为两个类别:静态代理和动态代理

静态代理: 也就是我们学习设计模式之代理模式时常见的事例,具体不在赘述,参见:【Java设计模式-13代理模式】

动态代理: 在静态代理中,因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。同时,一旦接口增加方法,目标对象与代理对象都要维护。那么如何解决这种问题呢?答案就是动态代理,下面会使用两种动态代理(jdk代理、cglib代理)分别实现同一个事例来体会一下动态代理的实现。

动态代理

动态代理具有以下特性:

1.代理对象不需要实现接口

2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

jdk代理

只支持对接口的的实现,其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

Proxy类主要用来获取动态代理对象,InvocationHandler接口用来约束调用者实现。
JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口

package com.lkf.parttern.proxy.dynamic; /** * 账户操作接口 * * @author kaifeng */ public interface Account { /** * 查询余额 */ void queryAccountBalance(); /** * 充值话费 */ void updateAccountBalance(); } package com.lkf.parttern.proxy.dynamic; /** * 账户操作实现 * * @author kaifeng */ public class AccountImpl implements Account { /** * 查询余额 */ @Override public void queryAccountBalance() { System.out.println("【AccountImpl::queryAccountBalance】-查询账户余额"); } /** * 充值话费 */ @Override public void updateAccountBalance() { System.out.println("【AccountImpl::updateAccountBalance】-话费充值"); } }

代理对象实现接口InvocationHandler

package com.lkf.parttern.proxy.dynamic.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 这里使用的是Jdk的动态代理,其必须实现接口,这也是jdk代理的缺陷,不过cglib代理会修补这个缺陷 * * @author kaifeng */ public class JDKAccountProxyFactory implements InvocationHandler { private Object target; /** * 代理方式实例化对象 */ public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object"); Object result = null; if (!objFlag) { System.out.println("【JDKAccountProxyFactory::invoke】-代理before"); } //真实方法调用 result = method.invoke(this.target, args); if (!objFlag) { System.out.println("【JDKAccountProxyFactory::invoke】-代理after"); } return result; } } package com.lkf.parttern.proxy.dynamic.jdk; import com.lkf.parttern.proxy.dynamic.Account; import com.lkf.parttern.proxy.dynamic.AccountImpl; /** * jdk动态代理测试 */ public class JDKAccountProxyFactoryTest { public static void main(String[] args) { Account account = (Account) new JDKAccountProxyFactory().bind(new AccountImpl()); account.queryAccountBalance(); System.out.println("***************************"); account.updateAccountBalance(); } } 【JDKAccountProxyFactory::invoke】-代理before 【AccountImpl::queryAccountBalance】-查询账户余额 【JDKAccountProxyFactory::invoke】-代理after *************************** 【JDKAccountProxyFactory::invoke】-代理before 【AccountImpl::updateAccountBalance】-话费充值 【JDKAccountProxyFactory::invoke】-代理after

CGLIB代理

对于上面说到JDK仅支持对实现接口的委托类进行代理的缺陷,CGLIB解决了这个问题,使其委托类也可是非接口实现类。
CGLIB内部使用到ASM,所以我们下面的例子需要引入cglib-3.2.5.jar

Cglib的原理是对指定的目标类动态生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类和final方法进行代理。

营业员实体类,没有实现接口的普通类

package com.lkf.parttern.proxy.dynamic; import java.util.UUID; /** * 营业员 * * @author kaifeng */ public class SalesPerson { private String jobNum = String.valueOf(UUID.randomUUID()); public String getJobNum() { return jobNum; } }

cglib动态代理实现了接口MethodInterceptor

package com.lkf.parttern.proxy.dynamic.cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * cglib动态生成代理对象 * * @author kaifeng */ public class CglibAccountProxyFactory implements MethodInterceptor { private Object target; /** * 获取对象实例 */ public Object getInstance(Object target) { this.target = target; return Enhancer.create(this.target.getClass(), this); } /** * 方法拦截 * <p> * 一般使用proxy.invokeSuper(obj,args)方法 * proxy.invoke(obj,args),这是执行生成子类的方法。 * 如果传入的obj就是子类的话,会发生内存溢出,因为子类的方法不进入intercept方法,而这个时候又去调用子类的方法,两个方法直接循环调用了 * </p> */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 排除Object类中的toString等方法 System.out.println(method.getDeclaringClass().getName()); boolean objFlag = method.getDeclaringClass().getName().equals("java.lang.Object"); if (!objFlag) { System.out.println("【CglibAccountProxyFactory::intercept】-代理before"); } Object result = null; result = methodProxy.invokeSuper(obj, args); if (!objFlag) { System.out.println("【CglibAccountProxyFactory::intercept】-代理after"); } return result; } }

cglib动态代理测试

package com.lkf.parttern.proxy.dynamic.cglib; import com.lkf.parttern.proxy.dynamic.Account; import com.lkf.parttern.proxy.dynamic.AccountImpl; import com.lkf.parttern.proxy.dynamic.SalesPerson; /** * cglib动态代理测试 * * @author kaifeng */ public class CglibAccountProxyFactoryTest { public static void main(String[] args) { // 下面是用cglib的代理 // 1.支持实现接口的类 Account account = (Account) new CglibAccountProxyFactory().getInstance(new AccountImpl()); account.queryAccountBalance(); // 2.支持未实现接口的类 SalesPerson salesPerson = (SalesPerson) new CglibAccountProxyFactory().getInstance(new SalesPerson()); System.out.println("我的工号是:" + salesPerson.getJobNum()); } } com.lkf.parttern.proxy.dynamic.AccountImpl 【CglibAccountProxyFactory::intercept】-代理before 【AccountImpl::queryAccountBalance】-查询账户余额 【CglibAccountProxyFactory::intercept】-代理after com.lkf.parttern.proxy.dynamic.SalesPerson 【CglibAccountProxyFactory::intercept】-代理before 【CglibAccountProxyFactory::intercept】-代理after 我的工号是:5cee95e6-9032-463c-8a4a-139d1f882b4f

总结

  • Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,会使用JDK代理;
如果目标对象没有实现接口,会使用Cglib代理

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java模式之一

代理模式的概念:对其他对象提供一种代理以控制对这个对象的访问代理模式的三种实现(1)静态代理静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.eg publicinterfacePammerDao{   voiddomain();}publicTargetimpl

java的动态代理

1\.什么是动态代理代理模式是为了提供额外或不同的操作,而插入的用来替代”实际”对象的对象,这些操作涉及到与”实际”对象的通信,因此代理通常充当中间人角色。Java的动态代理比代理的思想更前进了一步,它可以动态地创建并代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相

Spring的两种代理JDK和CGLIB的区别浅谈

一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以