聊聊Java动态代理(下)

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

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

CGLib动态代理

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

使用CGLib动态代理

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

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

聊聊Java动态代理(下)

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

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

聊聊Java动态代理(下)

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

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

聊聊Java动态代理(下)

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

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

聊聊Java动态代理(下)

其结果输出如下:

聊聊Java动态代理(下)

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

聊聊Java动态代理(下)

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

聊聊Java动态代理(下)

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

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

聊聊Java动态代理(下)

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

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

聊聊Java动态代理(下)

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


以上就是CGLib动态代理的全部内容了,如果觉得写得不错,可以扫描下方二维码关注本公众号。

聊聊Java动态代理(下)

如果想与各行各业的技术大牛一起指点江山激扬文字,可以在后台回复“进群”,即可获得与大牛近距离交流的机会。

此外,沉思君利用业余时间整理了海量的视频学习资源,涵盖前后端、大数据、机器学习、运维……在此免费赠送给各位爱学习的小伙伴们,领取方式很简单,只需在公众号后台回复“学习”二字或者点击文末“阅读原文”链接即可领取。