代理模式详解(静态代理和动态代理的区别以及联系)
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">service1</span><span class="hljs-params">()</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">service2</span><span class="hljs-params">()</span></span>;
}
接口含有两个抽象业务方法
- 被代理类
public class RealService implements IService {
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">service1</span><span class="hljs-params">()</span> </span>{
System.out.println(<span class="hljs-string">"service1"</span>);
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">service2</span><span class="hljs-params">()</span> </span>{
System.out.println(<span class="hljs-string">"service2"</span>);
}
}
被代理类实现业务接口,并且重写了业务方法。
- 日志代理
public class StaticLogProxy implements IService {
<span class="hljs-keyword">private</span> IService iService;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">StaticLogProxy</span><span class="hljs-params">(IService iService)</span> </span>{
<span class="hljs-keyword">this</span>.iService = iService;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">service1</span><span class="hljs-params">()</span> </span>{
System.out.println(<span class="hljs-string">"service1 start!"</span>);
iService.service1();
System.out.println(<span class="hljs-string">"service1 end!"</span>);
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">service2</span><span class="hljs-params">()</span> </span>{
System.out.println(<span class="hljs-string">"service2 start!"</span>);
iService.service2();
System.out.println(<span class="hljs-string">"service2 end!"</span>);
}
}
- 运行结果
如上图所示,在业务方法执行前后分别记了一条日志,表示业务方法执行的开始和结束。我们的代理类成功对原有业务方法做了增强。但是静态代理存在以下问题:
1.代理类和被代理类耦合,适用性差。试想如果我希望为所有的业务类添加日志增强逻辑,那么岂不是要为几乎每个业务类编写代理类?这是不现实也是开发时无法接受的。
2.代理类的增强逻辑和业务逻辑过于耦合,不利于后期维护和扩展。从service1和service2中就可以看出,除了中间的业务处理不一样,代理类的处理逻辑是一样的,而我们竟然没有将其分离。
以上两点其实反映的是同一个问题:我们希望自己编写的代理类对所有业务类,所有业务方法都适用,而静态代理的泛用性太差了。问题的关键在于编写代理逻辑和业务逻辑分离的代理类,运行时才将其和具体的业务类绑定,对其业务方法做增强。为此,我们需要动态代理。动态代理的实现方式很多,下面以jdk自带的代理方式做说明。
3. JDK动态代理详解
3.1 JDK动态代理实现
- 方法调用处理器
public class LogHandler implements InvocationHandler {
<span class="hljs-comment">//被代理对象</span>
<span class="hljs-keyword">private</span> Object target;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">LogHandler</span><span class="hljs-params">(Object target)</span> </span>{
<span class="hljs-keyword">this</span>.target = target;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">invoke</span><span class="hljs-params">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> Throwable </span>{
System.out.println(method.getName() + <span class="hljs-string">" start!"</span>);<span class="hljs-comment">//前置处理</span>
Object res = method.invoke(<span class="hljs-keyword">this</span>.target, args);<span class="hljs-comment">//执行业务方法</span>
System.out.println(method.getName() + <span class="hljs-string">" end!"</span>);<span class="hljs-comment">//后置处理</span>
<span class="hljs-keyword">return</span> res;
}
}
每一个代理对象都有一个与之关联的方法调用处理器,该处理器实现了InvocationHandler接口并重写了invoke方法。当我们调用代理对象的方法的时候,对该方法的调用会转交给方法调用处理器的invoke方法来执行,所以方法调用处理器的invoke方法是动态代理的核心。该方法内是通用的代理逻辑。在我们通过反射的方式通过被代理对象target执行业务逻辑的前后,可以对其作前置和后置增强。
- 客户端代码
public class Client {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(<span class="hljs-params">String[] args</span>) </span>{
<span class="hljs-comment">//1.创建被代理对象</span>
RealService realService = <span class="hljs-keyword">new</span> RealService();
<span class="hljs-comment">//2.创建动态代理的方法调用处理器</span>
LogHandler logHandler = <span class="hljs-keyword">new</span> LogHandler(realService);
<span class="hljs-comment">//3.创建动态代理对象</span>
IService service=(IService)Proxy.newProxyInstance(logHandler.getClass().getClassLoader(),
realService.getClass().getInterfaces(),logHandler);
service.service1();
System.<span class="hljs-keyword">out</span>.println(<span class="hljs-string">"---------------"</span>);
service.service2();
}
}
第一步我们创建了被代理对象realService;第二步我们创建了动态代理的核心:方法调用处理器。因为处理器内部需要委托被代理对象去执行真正的业务方法,所以需要传入被代理对象作参数。第三步我们通过调用反射包下的Proxy类的静态方法去生成真正的代理对象。该方法的方法签名如下
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
该方法含有三个参数
ClassLoader loader:指定加载的代理类的类加载器
Class<?>[] interfaces:指定代理类要实现的接口
InvocationHandler h:指定方法调用处理器
关于第二个参数可能要说明下,因为jdk的动态代理是针对接口的动态代理,代理类需要实现指定的业务接口,这里就是被代理类实现的那些接口。这样就能充分利用java多态特性,代码中所有能使用被代理对象的地方都能用代理对象进行替换。
- 运行结果
和静态代理的运行结果一样。如果我们要对其他业务类使用日志代理,只需要修改下客户端代码就行,这是静态代理办不到的。具有良好的扩展性是动态代理相比静态代理最大的优势。
3.2 方法调用流程图
- 1.客户端调用代理对象的业务方法,创建代理对象的时候传入了接口数组参数,故而代理对象也实现了业务接口。
- 2.代理对象将请求转发给方法调用处理器的invoke方法
- 3.方法调用处理器在invoke方法内部通过反射的方式调用被代理对象的业务方法
3.3 动态代理和静态代理的区别
- 静态代理编译期生成代理类;动态代理运行期生成代理类。
- 静态代理和被代理类及其业务逻辑耦合,适用性较差且代理逻辑难以扩展;动态代理可以在不知道被代理类的前提下编写代理逻辑,运行时才决定被代理对象,适用性好且代理逻辑易于扩展。
3.4 其他实现动态代理的方式
- cglib面向类的动态代理
- javaassist字节码操作库实现
- asm
4. 总结
代理用以控制对对象的访问,本质上是对其功能提供某种形式的增强。按实现又可分为静态代理和动态代理。动态代理因其代理逻辑和业务逻辑相分离的特点,具有良好的适用性和可扩展性,是Spring中AOP的底层实现。
</div>
<div class="postDesc">posted @ <span id="post-date">2018-07-09 18:19</span> <a href="https://www.cnblogs.com/takumicx/">takumiCX</a> 阅读(<span id="post_view_count">537</span>) 评论(<span id="post_comment_count">0</span>) <a href="https://i.cnblogs.com/EditPosts.aspx?postid=9285230" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(9285230);return false;">收藏</a></div>
</div>
<script src="//common.cnblogs.com/highlight/9.12.0/highlight.min.js"></script><script>markdown_highlight();</script><script type="text/javascript">var allowComments=true,cb_blogId=438438,cb_entryId=9285230,cb_blogApp=currentBlogApp,cb_blogUserGuid='fe0512c4-b476-451c-e955-08d5cfa66318',cb_entryCreatedDate='2018/7/9 18:19:00';loadViewCount(cb_entryId);var cb_postType=1;var isMarkdown=true;</script>
</div><!--end: forFlow -->
</div><!--end: mainContent 主体内容容器-->
<div id="sideBar">
<div id="sideBarMain">
<div id="blog-calendar" style=""><table id="blogCalendar" class="Cal" cellspacing="0" cellpadding="0" title="Calendar">
<tbody><tr><td colspan="7"><table class="CalTitle" cellspacing="0">
<tbody><tr><td class="CalNextPrev"><a href="javascript:void(0);" onclick="loadBlogCalendar('2019/03/01');return false;"><</a></td><td align="center">2019年4月</td><td class="CalNextPrev" align="right"><a href="javascript:void(0);" onclick="loadBlogCalendar('2019/05/01');return false;">></a></td></tr>
</tbody></table></td></tr><tr><th class="CalDayHeader" align="center" abbr="日" scope="col">日</th><th class="CalDayHeader" align="center" abbr="一" scope="col">一</th><th class="CalDayHeader" align="center" abbr="二" scope="col">二</th><th class="CalDayHeader" align="center" abbr="三" scope="col">三</th><th class="CalDayHeader" align="center" abbr="四" scope="col">四</th><th class="CalDayHeader" align="center" abbr="五" scope="col">五</th><th class="CalDayHeader" align="center" abbr="六" scope="col">六</th></tr><tr><td class="CalOtherMonthDay" align="center">31</td><td align="center">1</td><td align="center">2</td><td align="center">3</td><td align="center">4</td><td align="center">5</td><td class="CalWeekendDay" align="center">6</td></tr><tr><td class="CalWeekendDay" align="center">7</td><td align="center">8</td><td align="center">9</td><td align="center">10</td><td align="center">11</td><td align="center">12</td><td class="CalWeekendDay" align="center">13</td></tr><tr><td class="CalWeekendDay" align="center">14</td><td align="center">15</td><td align="center">16</td><td align="center">17</td><td align="center">18</td><td align="center">19</td><td class="CalWeekendDay" align="center">20</td></tr><tr><td class="CalWeekendDay" align="center">21</td><td align="center">22</td><td align="center">23</td><td align="center">24</td><td align="center">25</td><td align="center">26</td><td class="CalWeekendDay" align="center">27</td></tr><tr><td class="CalWeekendDay" align="center">28</td><td align="center">29</td><td class="CalTodayDay" align="center">30</td><td class="CalOtherMonthDay" align="center">1</td><td class="CalOtherMonthDay" align="center">2</td><td class="CalOtherMonthDay" align="center">3</td><td class="CalOtherMonthDay" align="center">4</td></tr><tr><td class="CalOtherMonthDay" align="center">5</td><td class="CalOtherMonthDay" align="center">6</td><td class="CalOtherMonthDay" align="center">7</td><td class="CalOtherMonthDay" align="center">8</td><td class="CalOtherMonthDay" align="center">9</td><td class="CalOtherMonthDay" align="center">10</td><td class="CalOtherMonthDay" align="center">11</td></tr>
<div id="leftcontentcontainer">
<div id="blog-sidecolumn"><div id="sidebar_search" class="sidebar-block">
</div><!--end: sideBarMain -->
</div><!--end: sideBar 侧边栏容器 -->
<div class="clear"></div>
</div><!--end: main -->
<div class="clear"></div>
<div id="footer">
Copyright ©2019 takumiCX
昵称:
不改了 退出 订阅评论
[Ctrl+Enter快捷键提交]
【培训】IT职业生涯指南,Java程序员薪资翻3倍的秘密
【推荐】专业便捷的企业级代码托管服务 - Gitee 码云
· 静态代理和动态代理的区别和联系
· 代理-静态代理和动态代理
· 代理模式(静态代理和动态代理)
· 代理模式(静态代理、动态代理)
· 静态代理、JDK动态代理和CGLib动态代理之前的区别
· 《科学大家》专栏 | “修剪基因”:让人绝望的遗传病将被改写
· Notch 不被欢迎参加《我的世界》十周年庆典
· 调薪在即 京东快递员们该走该留?
· 任正非谈招聘:耽误人家几年对得起人家吗
· 联想新机即将诞生:首发北斗高精度导航定位SoC HD8040
» 更多新闻...