聊聊Java动态代理(下)

前言

在之前的文章《聊聊Java动态代理(上)》中,笔者为大家介绍了Java原生的动态代理,并指出Java原生的动态代理有一个缺点就是被代理类必须显示地实现某个接口,否则无法正常使用,此局限性限制了Java原生动态代理的使用场景。幸好在Java生态中,实现动态代理的方式除了Java原生的动态代理,还有其他方式,本文将为大家介绍Java中另一种常见的动态代理实现方式——CGLib动态代理。

CGLib动态代理

CGLib是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为Java原生的动态代理提供了很好的补充。通常可以使用Java原生的动态代理创建代理,但如果要代理的类没有实现接口或者为了更好的性能,CGLib是一个好的选择。

使用CGLib动态代理

我们还是以前面的文章《聊聊Java动态代理(上)》中的用户登录功能为例,不清楚的朋友可以回过头先浏览一下该文章。

我们先实现基本登录功能:

BaseLoginService类只有一个login方法,默认所有用户都可以进行正常登录。注意该类与之前的文章《聊聊Java动态代理(上)》里的LoginServiceImpl的区别是LoginServiceImpl实现了LoginService接口,而BaseLoginService类并没有实现任何接口或者显示地继承某个类。

接下来我们定义我们的代理逻辑:

我们的代理逻辑是通过实现MethodInterceptor接口并实现intercept方法来实现的。在intercept方法的实现中,我们先对userId进行过滤,如果符合条件就继续调用正常的登录逻辑,否则就禁止用户登录。

然后我们还需要一个能够创建代理对象的功能:

代理类的创建需要使用Enhancer,其需要设置被代理对象的父类,由于被代理对象没有显示继承某个类,所以这里设置为被代理类本身就可以了。然后还需要设置代理逻辑,也就是上述LoginAdvice类的实例。

最后写一个客户端测一下:

其结果输出如下:

可以看到,使用CGLib我们也能实现动态代理。而且被代理类BaseLoginService并没有实现任何的接口,这一点是CGLib相对于Java原生动态代理的优势。但是CGLib对被代理类有没有其他要求呢?我们先来看下CGLib生成的代理类有什么特点,我们可以写一个客户端将该代理类的信息打印出来一探究竟。

我们将代理类的名称,还有其实现的接口以及继承的父类都打印出来,结果输出如下:

可以看到该类的名称并不是我们定义的,说明是在运行时产生的。其接口是net.sf.cglib.proxy.Factory,这是CGLib库中的一个接口。再看其父类是我们定义的BaseLoginService类,也就是说CGLib是通过继承被代理类来生成代理类,进而实现动态代理的。既然CGLib代理类是通过继承被代理类来生成的,那么如果被代理类的方法是final或者被代理类是final的还能不能实现代理逻辑呢?我们试一下就知道了。

首先我们将BaseLoginService类的login方法声明为final,代码就不贴出来了,我们直接看结果:

可以看到,客户端代码正常运行,只是这个时候已经没有了我们定义的代理逻辑了,“user1”和“user2”都正常登录了,因此如果被代理类的方法声明为final,CGLib是不能织入代理逻辑的。

接下来我们把BaseLoginService类声明为final试试看,同样我们看结果就行了:

可以看到,客户端代码直接报错了,所以如果被代理类被声明为final,CGLib会直接报错。

以上就是CGLib动态代理的全部内容了。

原文发布于微信公众号 - 编程沉思录(code-thinker)

原文发表时间:2018-02-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PPV课数据科学社区

python多线程编程(2): 线程的创建、启动、挂起和退出

如上一节,python 的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通...

26760
来自专栏python3

习题31:访问列表元素

访问列表中的元素,使用下标的方式,通常以0开始(为什么是0而不是1),这里程序的设计就是如此,个人觉得没有必要纠结,如有兴趣,可自行查看资料

9520
来自专栏蛋未明的专栏

json_decode在php中的一些无法解析的字符串

23950
来自专栏Astropeak

Spring使用 --- 基本概念(一):DI,依赖注入

12720
来自专栏奔跑的蛙牛技术博客

Java并发知识点(1)

对线程调用interrupt方法,线程中断状态将被置位(线程总会不断的检验这个标志,判断线程是否被中断),想要知道线程是否被置位,就要调用静态的方法

11540
来自专栏爱撒谎的男孩

struts核心配置文件详解(package)

34460
来自专栏菜鸟计划

angularjs 服务详解

一、服务 服务提供了一种能在应用的整改生命周期内保持数据的方法,它能够在控制器之间进行通信,并保持数据的一致性。 1.服务是一个单例对象,在每个应用中只会被实例...

36160
来自专栏顶级程序员

死磕 Java 并发 :Java 内存模型之 happens-before

来源:chenssy, cmsblogs.com/?p=2102 那么我们正确使用同步、锁的情况下,线程A修改了变量a何时对线程B可见? 我们无法就所有场景...

40450
来自专栏大内老A

.NET Core采用的全新配置系统[3]: “Options模式”下的配置是如何绑定为Options对象

配置的原子结构就是单纯的键值对,并且键和值都是字符串,但是在真正的项目开发中我们一般不会单纯地以键值对的形式来使用配置。值得推荐的做法就是采用《.NET Cor...

202100
来自专栏Java帮帮-微信公众号-技术文章全总结

03.线程安全/同步/线程通讯

03.线程安全/同步/线程通讯 一.一个典型的Java线程安全例子 ? ? 上面例子很容易理解,有一张银行卡,里面有1000的余额,程序模拟你和你老婆同时在取款...

44670

扫码关注云+社区

领取腾讯云代金券