http://blog.csdn.net/beijiguangyong/article/details/8624016

根据前面介绍的Proxy和InvocationHandler,实在很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制.

只要我们开发一个实际使用的软件系统,总会出现相同代码重复出现的情形,在这种情形下,最常见的做法是:选中那些代码一路“复制”、“粘贴”立即实现系统的功能,如果仅仅从软件功能上来看,他们确实已经完成了软件的开发。对于采用上述方法实现的系统,在软件开发期间可能会觉得无所谓,但如果有一天需要修改程序的公共部分,那意味着打开多份源代码进行修改。如果有100个地方,甚至是1000个地方使用了这段深色代码段,那修改维护这段代码的工作量将变成噩梦。

在这种情况下大部分少有经验的开发这都会将公共代码块定义成一个方法,然后让另外三段代码块直接调用该方法即可。这时如果需要修改公共的代码则只需要修改一次即可。而调用该方法的代码段,不管有多少地方调用了该方法,完全无须任何修改。只要被调用方法被修改了,所有调用该方法的地方自然改变了,通过这种方式大大降低了软件后期维护的复杂度。

但是这种方式最大的问题就是每个调用公共代码块的部分都和公共代码块有耦合,不利于调试和测试。最理想的效果就是公共的代码块既被执行又无须再程序中以硬编码方式直接被调用,这时候就可以通过动态代理的方式来达到这种效果。

由于JDK的动态代理只能创建指定接口的动态代理,所以下面先提供一个Dog接口,该接口代码非常简单,仅仅在该接口里定义了两个方法

  1. interface Dog {
  2. // info()方法声明
  3. public void info();
  4. // run()方法声明
  5. public void run();
  6. }

上面接口里只是简单定义了两个方法,并未提供方法实现。如果我们直接使用Proxy为该接口创建动态代理对象,则动态代理对象的所有方法的执行效果又将完全一样。在这种情况下,我们将先为该Dog接口提供一个简单的实现类:GunDog

  1. class GunDog implements Dog {
  2. // info方法实现,仅仅打印一个字符串
  3. @Override
  4. public void info() {
  5. System.out.println("我是一只猎狗");
  6. }
  7. // run方法实现,仅仅打印一个字符串
  8. @Override
  9. public void run() {
  10. System.out.println("我奔跑迅速");
  11. }
  12. }

上面代码没有丝毫的特别之处,该Dog的实现类仅仅为每个方法提供了一个简单实现。回到开始我们需要实现的功能:让公用的代码不以硬编码的方式出现在需要调用他的类中。此时我们假设info(),run()两个方法代表要调用公共代码的方法,那么要求:程序执行info(),run()方法时能调用某个通用的方法,但又不通过硬编码的方式调用该方法。下面提供DogUtil类该类中包含两个通用的方法。

  1. class DogUtil {
  2. // 第一个拦截器方法
  3. public void method1() {
  4. System.out.println("-----------模拟第一个通用方法-----------");
  5. }
  6. // 第二个拦截器方法
  7. public void method2() {
  8. System.out.println("-----------模拟第二个通用方法-----------");
  9. }
  10. }

借助于Proxy和InvocationHandler就可以实现:当程序调用info()方法和run()方法时,系统可以“自动”将method1()和method2()两个通用方法插入info()和run()方法执行中。

这个程序的关键在于下面的MyInvokationHandler类,该类是一个InvovationHandler实现类,该实现类的invoke方法将会作为代理的方法实现。

  1. class MyInvokationHandlerPro implements InvocationHandler {
  2. // 需要被代理的对象
  3. private Object target;
  4. public void setTarget(Object target) {
  5. this.target = target;
  6. }
  7. // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
  8. public Object invoke(Object proxy, Method method, Object[] args)
  9. throws Exception {
  10. DogUtil du = new DogUtil();
  11. // 执行DogUtil对象中method1方法
  12. du.method1();
  13. // 以target作为主调来执行method方法
  14. Object result = method.invoke(target, args);
  15. // 执行DogUtil对象中method2方法
  16. du.method2();
  17. return result;
  18. }
  19. }

上面程序实现了invoke方法时包含了一行关键代码,这行代码通过反射以target作为主调来执行method方法,这就是实现了调用target对象的原有方法。在粗体字代码之前调用DogUtil对象的method1()方法,在其后调用DogUtil对象的method2()方法。

下面为程序提供一个MyProxyFactory类,该对象专为指定的target生成动态代理实例。

  1. class MyProxyFactory {
  2. // 为指定target生成动态代理对象
  3. public static Object getProxy(Object target) throws Exception {
  4. // 创建一个MyInvokationHandler对象
  5. MyInvokationHandlerPro handler = new MyInvokationHandlerPro();
  6. // 为MyInvokationHandler设置target对象
  7. handler.setTarget(target);
  8. // 创建,并返回一个动态代理
  9. return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  10. target.getClass().getInterfaces(), handler);
  11. }
  12. }

上面动态代理工厂类提供了一个getProxy方法,该方法为target对象生成一个动态代理对象,这个动态代理对象与target实现了相同的接口,所以具有相同的public方法,从这个意义上来看,动态代理对象可以当成target对象使用。当程序调用动态代理对象的指定方法时,实际上将变成执行MyInvokationHandler对象的invoke方法。例如调用动态代理对象的info()方法,程序将开始执行invoke方法,其执行步骤如下

1、创建DogUtil实例

2、执行DogUtil实例的method1()方法。

3、使用反射以target作为调用者执行info()方法。

4、执行DogUtil实例的method2()方法。

看到上面执行的过程,读者应该已经发现:当我们使用动态代理对象代替target对象时,代理对象的方法就实现类前面的要求:程序执行info()、

run()方法时能调用method1()、method2()通用方法,但GunDog的方法中又没有以硬编码的方式调用method1()和method2()。

下面提供主程序来测试这种动态代理的效果

  1. public class TestProxy {
  2. public static void main(String[] args) throws Exception {
  3. // 创建一个原始的GunDog对象,作为target
  4. Dog target = new GunDog();
  5. // 以指定的target来创建动态代理
  6. Dog dog = (Dog) MyProxyFactory.getProxy(target);
  7. dog.info();
  8. dog.run();
  9. }
  10. }

上面程序中dog对象实际上是动态代理对象,只是该动态代理对象也实现类Dog接口,所以也可以当成Dog对象使用。程序执行dog的info()和run()方法时,实际上会先执行DogUtil的method1()在执行target对象的info()和run()方法最后在执行DogUtil的method2()执行结果如下图所示

通过上图来看不难发现采用动态代理可以非常灵活的实现解耦。通常而言,当我们使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的实际意义。通常都是为指定的目标对象来生成动态代理。

这种动态代理在AOP(Aspect Orient Program,面向切面编程)里被称为AOP代理,AOP代理可替代目标对象,AOP代理包含了目标对象的全部发那个发。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前,之后插入一些通用处理。

动态代理实现AOP【转】的更多相关文章

  1. 动态代理到基于动态代理的AOP

    动态代理,是java支持的一种程序设计方法. 动态代理实现中有两个重要的接口和类,分别是InvocationHandler(interface),Proxy(class). 要实现动态代理,必须要定义 ...

  2. .Net 动态代理,AOP

    .Net 动态代理,AOP 直接上代码了. /***************************************** * author:jinshuai * * E-mail:redfox ...

  3. 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)

    Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC   目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...

  4. Java动态代理-->Spring AOP

    引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Jav ...

  5. .Net 框架实现AOP(动态代理实现AOP,本文为翻译)

    在上一节,我们将静态实现AOP,但是对于一个大型项目,要想为每个类,每个方法都去实现AOP ,进行日志记录和权限验证似乎是不可能的. 即使可能对于成百上千个类维护,也是很难维护.所以今天的主题就是如标 ...

  6. Java使用动态代理实现AOP

    参考资料: http://www.importnew.com/15420.htmlhttp://www.cnblogs.com/techyc/p/3455950.html Spring是借助了动态代理 ...

  7. DispatchProxy实现动态代理及AOP

    DispatchProxy类是DotnetCore下的动态代理的类,源码地址:Github,官方文档:MSDN.主要是Activator以及AssemblyBuilder来实现的(请看源码分析),园子 ...

  8. Java 动态代理与AOP

    动态代理与AOP 代理模式 代理模式给某一个目标对象(target)提供代理对象(proxy),并由代理对象控制对target对象的引用. 模式图: 代理模式中的角色有: 抽象对象角色(Abstrac ...

  9. Java 动态代理及AOP实现机制

    AOP实现机制http://www.iteye.com/topic/1116696 AOP: (Aspect Oriented Programming) 面向切面编程AOP包括切面(aspect).通 ...

随机推荐

  1. DMALL刘江峰:生鲜市场具有巨大O2O改造空间

    今日,全球移动互联网大会(GMIC)在北京-国家会议中心开幕.DMALL创始人刘江峰在全球O2O峰会论坛作主题发言时谈到,生鲜市场具有巨大O2O改造空间.同时这也是刘江峰在离开荣耀之后首次登台介绍自己 ...

  2. oracle ORA-01747(系统保留关键字)user.table.column, table.column 或列说明无效 hibernate映射oracle保留关键字

    1.查询系统关键 select * from v$reserved_words 确认你使用的是否为关键字: select * from v$reserved_words w where w.KEYWO ...

  3. mysqldump: Got error: 1556: You can't use locks with log tables. when using LOCK TABLES

    mysqldump: Got error: 1556: You can't use locks with log tables. when using LOCK TABLES 我是把一些mysqldu ...

  4. 搜索关注点--2014年的google关注点

    补充: 网站知名网站收录 网站被世界三大知名网站DMOZ,Yahoo和Looksmart收录 众所周知,Google的Pagerank系统对那些门户网络目录如 DMOZ,Yahoo和Looksmart ...

  5. HDP2.4安装(五):集群及组件安装

    HDP(Hortonworks Data Platform)是hortworks推出的100%开源的hadoop发行版本,以YARN 作为其架构中心,包含pig.hive.phoniex.hbase. ...

  6. VMware 虚拟机Red Hat 5.9 交换区及硬盘空间调整

    首先要通过VMware设置简单实现内存扩大.但是系统中的/swap应该如何设置呢? 1. 创建swap 文件 使用如下命令: #dd if=/dev/zero of=/swap/swapfile bs ...

  7. WPF--Calendar控件高级使用

    一.得到当前显示的月份: DateTime SelectedDay = this.MC.DisplayDate; 二.得到当前选中的天,得到当前选中的周,得到当前显示的月份: 如果你使用系统默认的事件 ...

  8. LitDB文章

    阅读目录 1.LiteDB初步介绍 2.LiteDB使用基本案例 3.LiteDB的技术细节 4.资源其他 今天给大家介绍一个不错的小巧轻量级的NoSQL文件数据库LiteDB.本博客在2013年也介 ...

  9. 【linux】之安装mysql常用配置

    下载mysql地址 http://dev.mysql.com/downloads/mysql/ 选择下面这个 查看是否存在mysql安装包 rpm -qa|grep -i mysql 删除mysql安 ...

  10. [tty与uart]1.Linux中tty框架与uart框架之间的调用关系剖析

    转自:http://developer.51cto.com/art/201209/357501_all.htm 目录 1.tty框架 2.uart框架 3.自底向上 4.自顶向下 5.关系图 在这期间 ...