最近,小组同事做代码改造时,使用到了动态代理,自己阅读时,发现对代理这种设计模式都不怎么清楚,导致理解代码也很困难 自己唯一能看懂的,大概就是handler中的invoke方法 ,文中作出了非常详细的介绍,需要的朋友可以参考下
一、代理设计模式
1.1 什么是代理
- 考虑真实的编程场景,项目中存在一个访问其他数据源的接口,包含一个
query()
方法 - 我们已经针对这个接口,实现了MySQL、Hive、HBase、MongoDB等作为数据源的实现类
- 但是,在测试过程中,我们发现这些数据源的查询并不是很稳定
- 最原始的想法: 在所有实现类
query()
方法中,代码首部获取startTime,代码尾部获取endTime,通过打印日志的方式,知道每次查询的耗时
long startTime = System.currentTimeMillis(); logger.info("query mysql start:" + new Date(startTime).toLocaleString()); // 具体的query代码 ... long endTime = System.currentTimeMillis(); logger.info(String.format("query mysql end: %s, consumed time: %dms", new Date(endTime).toLocaleString(), (endTime-startTime)));
- 直接修改已经实现的方法,存在很多缺点:
(1)现在是打印日志,代码非常简单,就算query()
方法不是你实现的,你也能很好的完成。
(2)但是如果是其他功能呢?比如,如果查询失败,要求你查询重试
- 因此,在不改变已经实现好的
query()
方法前提下,去实现日志打印的功能是最好的方法。 - 进阶想法: 我为每个实现类创建一个
包装
类。
(1)这个包装
类与实现类一样,实现了相同的接口。
(2)在query()
方法中,直接调用实现类的query()
方法,并在调用前后进行日志打印
(3)对实现类方法的调用,都改成对包装
类方法的调用
long startTime = System.currentTimeMillis(); logger.info("query mysql start:" + new Date(startTime).toLocaleString(<b style="color:transparent">来源gao@!dai!ma.com搞$$代^@码!网</b>)); // 使用try-finally JSONObject[] data = null; try { data = mysql.query(); return data; } catch (Exception exception) { throw exception; } finally { long endTime = System.currentTimeMillis(); logger.info(String.format("query mysql end: %s, consumed time: %dms", new Date(endTime).toLocaleString(), (endTime - startTime))); }
- 这时,代理模式的概念就变得非常清晰了:不直接调用实现类的某个方法,而是通过实现类的代理去调用。
- 这样不仅可以实现调用者与被调用者之间的解耦合,还可以在不修改调用者的情况下,丰富功能逻辑。
1.2 代理模式入门
代理模式的UML图如下
1.subject:
抽象主题角色,是一个接口,定义了一系列的公共对外方法
2.real subject:
真实主题角色,也就是我刚刚提到的实现类,又称委托类。
委托类实现抽象主题,负责实现具体的业务逻辑
3.proxy:
代理主题角色,简称代理类。它也实现了抽象主题,用于代理、封装,甚至增强委托类。
一般通过内含委托类,实现对委托类的封装
4.client:
当访问具体的业务逻辑时,clinet表面是访问代理类,而实际是访问被代理类封装的委托类
代理模式的应用场景:目前,就我本人所接触的使用场景,就是通过代理去打印日志、增强业务逻辑
以上就是浅谈Java动态代理的实现的详细内容,更多请关注gaodaima搞代码网其它相关文章!