JDK 动态代理与 CGLIB 动态代理,它俩真的不一样
摘要:一文带你搞懂JDK 动态代理与 CGLIB 动态代理
本文分享自华为云社区《一文带你搞懂JDK 动态代理与 CGLIB 动态代理》,作者: Code皮皮虾 。
两者有何区别
1、Jdk动态代理:利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
所以:
- 如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用;
- 如果想要使用CGlib动态代理,那么代理类不能使用final修饰类和方法;
还有: 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。
如何实现
JDK动态代理
UserService接口
public interface UserService { void addUser(); void updateUser(String str); }
UserServiceImpl实现类
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("添加用户"); } @Override public void updateUser(String str) { System.out.println("更新用户信息" + str); } }
UserProxy代理类,实现InvocationHandler接口重写invoke方法
public class UserProxy implements InvocationHandler { private Object target; public UserProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object res = method.invoke(target, args); System.out.println("记录日志"); return res; } }
test测试类
public class test { public static void main(String[] args) { UserServiceImpl impl = new UserServiceImpl(); UserProxy userProxy = new UserProxy(impl); UserService userService = (UserService) Proxy.newProxyInstance(impl.getClass().getClassLoader(),impl.getClass().getInterfaces(),userProxy); userService.addUser(); userService.updateUser(":我是皮皮虾"); } }
可见实现了增强,打印出记录日志
CGlib动态代理
CGlib不像是JDK动态代理,CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
UserServiceImpl被代理类
public class UserServiceImpl { public void addUser() { System.out.println("添加了一个用户"); } public void deleteUser() { System.out.println("删除了一个用户"); } }
UserServiceCGlib代理
public class UserServiceCGlib implements MethodInterceptor { private Object target; public UserServiceCGlib() { } public UserServiceCGlib(Object target) { this.target = target; } //返回一个代理对象: 是 target对象的代理对象 public Object getProxyInstance() { //1. 创建一个工具类 Enhancer enhancer = new Enhancer(); //2. 设置父类 enhancer.setSuperclass(target.getClass()); //3. 设置回调函数 enhancer.setCallback(this); //4. 创建子类对象,即代理对象 return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("增强开始~~~"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("增强结束~~~"); return result; } }
test测试类
public class test { public static void main(String[] args) { UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl()); UserServiceImpl userService = (UserServiceImpl)serviceCGlib.getProxyInstance(); userService.addUser(); System.out.println(); userService.deleteUser(); } }
可见实现了增强,打印出记录日志
使用场景
到这里相信各位小伙伴们已经基本掌握了JDK动态代理和CGlib动态代理的区别和实现
但是,如果是在面试过程中,除了要答出以上要点,你还要回答出它们的使用场景,这其实就是面试的加分项
那么,这两个动态代理的使用场景是什么呢???
答案:Spring AOP
以下是Spring AOP创建代理的方法
@Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } //如果 if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理
2、如果目标对象实现了接口,也可以强制使用CGLIB
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如果需要强制使用CGLIB来实现AOP,需要配置spring.aop.proxy-target-class=true或@EnableAspectJAutoProxy(proxyTargetClass = true

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
专有云容灾难题这样破!
影响企业 IT 业务系统连续性原因有很多,病毒威胁、硬件故障、人为误操作、电源空调故障。更有甚者遇到外部不可抗力,如台风、地震、水灾、火灾、爆炸、光纤挖断… 企业 IT 若想逃过这么多的“劫难”,平稳运行,实属不易! 怎么确保在极端情况下减少或避免 IT 系统故障? 建设灾备数据中心势在必行,提前进行系统化灾备架构设计,以应对灾难发生。 在云还“未成气候”的时候,企业传统架构容灾尚停留在“数据备份”层面,通过建设本地或异地容灾中心,进行数据容灾备份,发生灾难时应用中断。今天,随着云计算技术发展,新技术衍生,使得云容灾具有更完备的能力。 迎接安全挑战,完善容灾体系,面对企业在容灾方面的种种困惑,本文将重点介绍专有云容灾的技术和实践,详解容灾背景、标准以及百度智能云的专有云容灾解决方案,旨在为企业容灾带来新思考。 >>> 专有云容灾,兼具多种能力 由于多租户、虚拟化、资源池等技术特性,云计算平台在灾难恢复的影响评估、关键指标、技术要求、组织管理等方面与传统架构存在诸多差异。 百度智能云专有云具备强大的灾备能力,依托云计算、AI、大数据等技术,百度智能云可为客户提供数据备...
- 下一篇
Redis 很屌,不懂使用规范就糟蹋了
这可能是最中肯的 Redis 使用规范了 码哥,昨天我被公司 Leader 批评了。 我在单身红娘婚恋类型互联网公司工作,在双十一推出下单就送女朋友的活动。 谁曾想,凌晨 12 点之后,用户量暴增,出现了一个技术故障,用户无法下单,当时老大火冒三丈! 经过查找发现 Redis 报 Could not get a resource from the pool。 获取不到连接资源,并且集群中的单台 Redis 连接量很高。 于是各种更改最大连接数、连接等待数,虽然报错信息频率有所缓解,但还是持续报错。 后来经过线下测试,发现存放 Redis 中的字符数据很大,平均 1s 返回数据。 码哥,可以分享下使用 Redis 的规范么?我想做一个唯快不破的真男人! 通过 Redis 为什么这么快?这篇文章我们知道 Redis 为了高性能和节省内存费劲心思。 所以,只有规范的使用 Redis,才能实现高性能和节省内存,否则再屌的 Redis 也禁不起我们瞎折腾。 Redis 使用规范围绕如下几个纬度展开: 键值对使用规范; 命令使用规范; 数据保存规范; 运维规范。 键值对使用规范 有两点需要注意: ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS8编译安装MySQL8.0.19
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Docker使用Oracle官方镜像安装(12C,18C,19C)