2011-12-07 11:39 6108人阅读 评论(0) 收藏 举报
第一部分,您看到了如何使用Spring AOP来实现跟踪和记录方面。跟踪和记录都是“消极”方面,因为它们的出现并不会对应用程序的其他行为产生影响。它们都使用了消极的before和after形式的通知。

  但是如果您希望改变应用程序的常规行为呢?例如说,您希望重写一个方法?这样的话,您就需要使用更积极的around形式的通知。

  第一部分的简单例子应用程序包括IbusinessLogic接口、BusinessLogic类和MainApplication类,如下所示:

  1. public interface IBusinessLogic
  2. {
  3. public void foo();
  4. }
  5. public class BusinessLogic
  6. implements IBusinessLogic
  7. {
  8. public void foo()
  9. {
  10. System.out.println(
  11. "Inside BusinessLogic.foo()");
  12. }
  13. }
  14. import org.springframework.context.ApplicationContext;
  15. import org.springframework.context.support.FileSystemXmlApplicationContext;
  16. public class MainApplication
  17. {
  18. public static void main(String [] args)
  19. {
  20. // Read the configuration file
  21. ApplicationContext ctx =
  22. new FileSystemXmlApplicationContext(
  23. "springconfig.xml");
  24. //Instantiate an object
  25. IBusinessLogic testObject =
  26. (IBusinessLogic) ctx.getBean(
  27. "businesslogicbean");
  28. // Execute the public
  29. // method of the bean
  30. testObject.foo();
  31. }
  32. }

  要对一个BusinessLogic类的实例彻底重写对foo()方法的调用,需要创建around通知,如下面的AroundAdvice类所示:

  1. import org.aopalliance.intercept.MethodInvocation;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. public class AroundAdvice
  4. implements MethodInterceptor
  5. {
  6. public Object invoke(
  7. MethodInvocation invocation)
  8. throws Throwable
  9. {
  10. System.out.println(
  11. "Hello world! (by " +
  12. this.getClass().getName() +
  13. ")");
  14. return null;
  15. }
  16. }

  要在spring中用作around通知,AroundAdvice类必须实现MethodInterceptor接口和它的invoke(..)方法。每当截获到方法的重写,invoke(..)方法就会被调用。最后一步是改变包含在应用程序的springconfig.xml文件中的Spring运行时配置,以便可以对应用程序应用AroundAdvice。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE beans PUBLIC  "-//SPRING//DTD BEAN//EN"
  3. "http://www.springframework.org/dtd/spring-beans.dtd">
  4. <beans>
  5. <!-- Bean configuration -->
  6. <bean id="businesslogicbean"
  7. class="org.springframework.aop.framework.ProxyFactoryBean">
  8. <property name="proxyInterfaces">
  9. <value>IBusinessLogic</value>
  10. </property>
  11. <property name="target">
  12. <ref local="beanTarget"/>
  13. </property>
  14. <property name="interceptorNames">
  15. <list>
  16. <value>theAroundAdvisor</value>
  17. </list>
  18. </property>
  19. </bean>
  20. <!-- Bean Classes -->
  21. <bean id="beanTarget"
  22. class="BusinessLogic"/>
  23. <!-- Advisor pointcut definition for around advice -->
  24. <bean id="theAroundAdvisor"
  25. class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  26. <property name="advice">
  27. <ref local="theAroundAdvice"/>
  28. </property>
  29. <property name="pattern">
  30. <value>.*</value>
  31. </property>
  32. </bean>
  33. <!-- Advice classes -->
  34. <bean id="theAroundAdvice"
  35. class="AroundAdvice"/>
  36. </beans>

  根据该springconfig.xml配置文件,theAroundAdvisor截获所有对BusinessLogic类的方法的调用。接下来,theAroundAdvisor被关联到theAroundAdvice,表明当截获一个方法时,就应该使用在AroundAdvice类中指定的通知。既然已经指定了around通知的正确配置,下一次执行MainApplication类时,BusinessLogic bean的foo()方法就会被截获并重写,如图3所示:


图3. 使用around通知重写对BusinessLogic类中的foo()方法的调用

前面的例子显示,BusinessLogic类中的foo()方法可以通过AroundAdvice类中的invoke(..)方法彻底重写。原来的foo()方法完全不能被invoke(..)方法调用。如果希望从around通知内调用foo()方法,可以使用proceed()方法,可从invoke(..)方法的MethodInvocation参数中得到它。

  1. public class AroundAdvice
  2. implements MethodInterceptor
  3. {
  4. public Object invoke(
  5. MethodInvocation invocation)
  6. throws Throwable
  7. {
  8. System.out.println(
  9. "Hello world! (by " +
  10. this.getClass().getName() +
  11. ")");
  12. invocation.proceed();
  13. System.out.println("Goodbye! (by " +
  14. this.getClass().getName() +
  15. ")");
  16. return null;
  17. }
  18. }

  图4显示了对proceed()的调用如何影响操作的顺序(与图3所示的初始around通知执行相比较)。


图4. 从around通知内使用proceed()调用原来的方法

  当调用proceed()时,实际是在指示被截获的方法(在本例中是foo()方法)利用包含在MethodInvocation对象中的信息运行。您可以通过调用MethodInvocation类中的其他方法来改变该信息。

您可能希望更改包含在MethodInvocation类中的信息,以便在使用proceed()调用被截获的方法之前对被截获方法的参数设置新值。

  通过对MethodInvocation对象调用getArguments()方法,然后在返回的数组中设置其中的一个参数对象,最初传递给被截获的方法的参数可以被更改。

  如果IbusinessClass和BusinessLogic类的foo()方法被更改为使用整型参数,那么就可以将传递给被截获的调用的值由在AroundAdvice的notify(..)方法中传递改为在foo(int)中传递。

  1. public class AroundAdvice
  2. implements MethodInterceptor
  3. {
  4. public Object invoke(
  5. MethodInvocation invocation)
  6. throws Throwable
  7. {
  8. System.out.println(
  9. "Hello world! (by " +
  10. this.getClass().getName() +
  11. ")");
  12. invocation.getArguments()[0] = new Integer(20);
  13. invocation.proceed();
  14. System.out.println(
  15. "Goodbye! (by " +
  16. this.getClass().getName() +
  17. ")");
  18. return null;
  19. }
  20. }

  在本例中,被截获的方法的第一个形参被假设为int。实参本身是作为对象传递的,所以通过将其包装在Integer类实例中的方法,基本的int类型的形参被改为对应数组中的新值。如果您将该参数设置为一个非Integer对象的值,那么在运行时就会抛出IllegalArgumentException异常。

  您还将注意到,invoke(..)方法必须包含一个return语句,因为该方法需要返回值。但是,被重写的foo()方法并不返回对象,所以invoke(..)方法可以以返回null结束。如果在foo()方法不需要的情况下,您仍然返回了一个对象,那么该对象将被忽略。

  如果foo()方法确实需要返回值,那么需要返回一个与foo()方法的初始返回类型在同一个类或其子类中的对象。如果foo()方法返回一个简单类型,例如,一个integer,那么您需要返回一个Integer类的对象,当方法被重写时,该对象会自动由AOP代理拆箱,如图5所示:


图5. around通知的装箱和自动拆箱

  图字:

  Object invoke:对象调用

  The integer return value is boxed in a Integer object in the AroundAdvice and then unboxed by the AOP Proxy:整型返回值被装箱在AroundAdvic通知的一个Integer对象中,然后由AOP代理拆箱。

  面向方面编程还是一个比较新的领域,尤其是与衍生出它的面向对象编程相比。设计模式通常被认为是常见问题的通用解决方案,因为面向方面发展的时间还不长,所以已发现的面向方面设计模式比较少。

  此处要介绍的是一种正在浮现的模式,即Cuckoo's Egg设计模式。该模式还有其他的叫法,它在面向对象领域的对等体包括模仿对象(Mock Object)和模仿测试(Mock Testing),甚至代理模式也与它有一些类似之处。

  Cuckoo's Egg面向方面设计模式可以被定义为应用程序上下文中功能部件的透明和模块化的置换。就像杜鹃偷偷地把自己的蛋放在另一种鸟的巢中一样,Cuckoo's Egg设计模式用一个替代功能部件实现置换现有的功能部件,而使造成的干扰尽可能少。

  这种置换的实现方式可以是静态的、动态的、部分的、完全的,针对一个对象的多个部分,或针对多个组件。使用面向方面的方法可以透明地实现功能部件的置换,而无需对应用程序的其余部分进行更改。要置换应用程序中现有功能部件的替代功能部件就是“杜鹃的蛋”。图6显示了Cuckoo's Egg设计模式中的主要组成元素。


图6. Cuckoo's Egg设计模式中的主要组成元素

  图字:

  Application:应用程序

  Component:组件

  Replacement Feature:替代功能部件

  Component 1 and 2 together encompass a distinct feature of the software:组件1和2共同包含了软件的一个独立的功能部件

  The Cuckoo's Egg pattern transparently replaces an existing feature of the software:Cuckoo's Egg模式透明地置换了软件现有的功能部件

  Before the pattern is applied:应用该模式前

  After the pattern is applied:应用该模式后

  Cuckoo's Egg设计模式依赖于around通知的概念。您需要借助于积极的和侵入性的around通知来截获并有效置换应用程序中现有的功能部件。

  有关Cuckoo's Egg设计模式的更多信息,以及AspectJ中的一个可选实现,请参见《AspectJ Cookbook》(O'Reilly,2004年12月出版)。

 要使用Spring AOP实现Cuckoo's Egg设计模式,需要声明一个around通知来截获所有对要置换的功能部件的调用。与hot-swappable target sources(Spring AOP的一个功能部件,将在本系列的另一篇文章中介绍)不同,around通知的显式使用使得Cuckoo's Egg实现可以有效地跨越对象边界(因此也可以跨越bean边界)进行整个功能部件的置换,如图7所示。


图7. 一个跨越bean边界的组件

  图字:

  A feature crosses the boundaries of BusinessLogic and BusinessLogic2 by depending on behavior supplied separately by the two beans:一个功能部件通过依赖于由BusinessLogic和BusinessLogic2各自提供的行为而跨越了这两个bean的边界

  下面的代码显示了一个具有两个bean的简单应用程序,其中有一个功能部件跨越了该应用程序的多个方面。要置换的功能部件可以被视为包含IBusinessLogic bean中的foo()方法和IBusinessLogic2 bean中的bar()方法。IBusinessLogic2 bean中的baz()方法不是 该功能部件的一部分,所以不进行置换。

  1. public interface IBusinessLogic
  2. {
  3. public void foo();
  4. }
  5. public interface IBusinessLogic2
  6. {
  7. public void bar();
  8. public void baz();
  9. }

  此处,ReplacementFeature类扮演了“杜鹃的蛋”的角色,它提供了将被透明地引入应用程序的替代实现。ReplacementFeature类实现了所有在该类引入时要被置换的方法。

  1. public class ReplacementFeature
  2. {
  3. public void foo()
  4. {
  5. System.out.println(
  6. "Inside ReplacementFeature.foo()");
  7. }
  8. public void bar()
  9. {
  10. System.out.println(
  11. "Inside ReplacementFeature.bar()");
  12. }
  13. }

  现在需要声明一个around通知来截获对跨越bean的功能部件的方法调用。CuckoosEgg类提供了某种around通知来检查被截获的方法,并将适当的方法调用传递给ReplacementFeature类的实例。

  1. public class CuckoosEgg implements MethodInterceptor
  2. {
  3. public ReplacementFeature replacementFeature =
  4. new ReplacementFeature();
  5. public Object invoke(MethodInvocation invocation)
  6. throws Throwable
  7. {
  8. if (invocation.getMethod().getName().equals("foo"))
  9. {
  10. replacementFeature.foo();
  11. }
  12. else
  13. {
  14. replacementFeature.bar();
  15. }
  16. return null;
  17. }
  18. }

  因为与Spring框架关系密切,Cuckoo's Egg设计的详细信息被放在springconfig.xml配置文件中。对springconfig.xml文件的更改将确保所有对IbusinessLogic和IBusinessLogic2 bean的foo()方法和bar()方法的调用都将被截获,并传递给CuckoosEgg类的around通知。

  1. ...
  2. <!--CONFIG-->
  3. <bean id="businesslogicbean"
  4. class="org.springframework.aop.framework.ProxyFactoryBean">
  5. <property name="proxyInterfaces">
  6. <value>IBusinessLogic</value>
  7. </property>
  8. <property name="target">
  9. <ref local="beanTarget"/>
  10. </property>
  11. <property name="interceptorNames">
  12. <list>
  13. <value>theCuckoosEggAdvisor</value>
  14. </list>
  15. </property>
  16. </bean>
  17. <bean id="businesslogicbean2"
  18. class="org.springframework.aop.framework.ProxyFactoryBean">
  19. <property name="proxyInterfaces">
  20. <value>IBusinessLogic2</value>
  21. </property>
  22. <property name="target">
  23. <ref local="beanTarget2"/>
  24. </property>
  25. <property name="interceptorNames">
  26. <list>
  27. <value>theCuckoosEgg2Advisor</value>
  28. </list>
  29. </property>
  30. </bean>
  31. <!--CLASS-->
  32. <bean id="beanTarget" class="BusinessLogic"/>
  33. <bean id="beanTarget2" class="BusinessLogic2"/>
  34. <!--ADVISOR-->
  35. <bean id="theCuckoosEggAdvisor"
  36. class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  37. <property name="advice">
  38. <ref local="theReplacementFeaturePart1Advice"/>
  39. </property>
  40. <property name="pattern">
  41. <value>IBusinessLogic.*</value>
  42. </property>
  43. </bean>
  44. <bean id="theCuckoosEgg2Advisor"
  45. class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  46. <property name="advice">
  47. <ref local="theReplacementFeaturePart2Advice"/>
  48. </property>
  49. <property name="pattern">
  50. <value>IBusinessLogic2.bar*</value>
  51. </property>
  52. </bean>
  53. <!--ADVICE-->
  54. <bean id="theReplacementFeaturePart1Advice" class="CuckoosEgg"/>
  55. <bean id="theReplacementFeaturePart2Advice" class="CuckoosEgg"/>
  56. ...

  当使用修改后的springconfig.xml文件运行例子应用程序时,要替换的、被指定为功能部件的一部分的方法调用完全被截获并传递给ReplacementFeature类。

  通常,即使在同一个实现环境中,我们也可以用不同的方法来实现同一种设计模式。实现上例的另一种方法是实现两个独立的通知。

  最后需要注意的是,使用Cuckoo's Egg设计模式置换的功能部件,不管它是跨越bean的还是在一个类中,它的生命周期与它所置换的功能部件的目标生命周期匹配。在上例中这没什么问题,因为只有一个功能部件实例被置换了,而且唯一的Cuckoo's Egg通知只维护一个替代功能部件。

  这个例子非常简单,而在实践中,您很可能必须处理大量需要用各自的Cuckoo's Egg实例置换的功能部件实例。在这种情况下,单个的方面实例需要被关联到单个的要置换的功能部件实例。本系列的下一篇文章将会考虑方面生命周期的用法,届时将解决这个问题。

  结束语

  本文介绍了如何在Spring框架内谨慎使用around形式的通知。around形式的通知常用于实现Cuckoo's Egg设计模式时,所以我们引入了一个例子来说明如何使用Spring AOP实现这种面向方面设计模式。

  在本系列的第三部分中,您将看到如何使用Spring框架中其他的AOP基本概念。这些概念包括:控制方面生命周期、使用基于introduction通知的积极方面改变应用程序的静态结构,以及使用control flow切入点实现对方面编织的更细微的控制。

转-Spring Framework中的AOP之around通知的更多相关文章

  1. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->关于spring framework中的beans

    Spring framework中的beans 1.概述 bean其实就是各个类实例化后的对象,即objects spring framework的IOC容器所管理的基本单元就是bean spring ...

  2. Spring Framework中常见的事务传播陷阱(译文)

    最近看到Medium上一篇讨论Spring Framework中事务传播的文章,解释了几种常见的问题,解释的不错,这里直接翻译吧(意译为主,粗体和斜体是我自己加上的). 译文: 这是我的第一篇文章,我 ...

  3. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework中的spring web MVC模块

    spring framework中的spring web MVC模块 1.概述 spring web mvc是spring框架中的一个模块 spring web mvc实现了web的MVC架构模式,可 ...

  4. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework中web相关的知识(概述)

    Spring Framework中web相关的知识 1.概述: 参考资料:官网documentation中第22小节内容 关于spring web mvc:  spring framework中拥有自 ...

  5. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  6. Spring Boot中使用AOP记录请求日志

    这周看别人写的springboot后端代码中有使用AOP记录请求日志,以前没接触过,因此学习下. 一.AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编 ...

  7. 46. Spring Boot中使用AOP统一处理Web请求日志

    在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...

  8. (转)Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  9. spring中的AOP 以及各种通知 配置

    理解了前面动态代理对象的原理之后,其实还是有很多不足之处,因为如果在项目中有20多个类,每个类有100多个方法都需要判断是不是要开事务,那么方法调用那里会相当麻烦. spring中的AOP很好地解决了 ...

随机推荐

  1. iOS架构师之路:控制器(View Controller)瘦身设计

    前言 古老的MVC架构是容易被iOS开发者理解和接受的设计模式,但是由于iOS开发的项目功能越来越负责庞大,项目代码也随之不断壮大,MVC的模糊定义导致我们的业务开发工程师很容易把大量的代码写到视图控 ...

  2. 记AppStore 被打回的经历

    在快驰已然有半年时间之久. 见证了“快货运”产品,在不断摧残的环境中成长着.  两个人,将一个产品亲手从无到有的构建,有过心酸.有过累和有过憔悴,但当“快货运”开始上APP store时,又让人觉得开 ...

  3. jQuery原生框架中的jQuery.fn.extend和jQuery.extend

    extend 方法在 jQuery 中是一个很重要的方法,jQuey 内部用它来扩展静态方法或实例方法,而且我们开发 jQuery 插件开发的时候也会用到它.但是在内部,是存在 jQuery.fn.e ...

  4. vs2013发布网站

    第一次在Server2008中发布网站,期间发生了很多的错误,这里记录下来,以供以后的学习. (1).首先在IIS上先建一个网站,(网站名称.物理路径.类型 IP地址 和端口)然后点击确认,这样就是先 ...

  5. Cannot forward after response has been committed

    项目:蒙文词语检索 日期:2016-05-01 提示:Cannot forward after response has been committed 出处:request.getRequestDis ...

  6. 30+有用的CSS代码片段

    在一篇文章中收集所有的CSS代码片段几乎是不可能的事情,但是我们这里列出了一些相对于其他的更有用的代码片段,不要被这些代码的长度所吓到,因为它们都很容易实现,并且具有良好的文档.除了那些解决常见的恼人 ...

  7. (转)浅析JS运行机制

    原文 从一个简单的问题谈起: 1 <script type="text/javascript"> 2 alert(i); // ? 3 var i = 1; 4 < ...

  8. ArrowLayer : A coustom layer animation

    Since my other answer (animating two levels of masks) has some graphics glitches, I decided to try r ...

  9. Azure自动化实例: 复制blog用于备份

    在Azure 自动化:使用PowerShell Credential连接到Azure, 之后, 我在项目中遇到了实现blog备份的任务, 现将其作为一个实例写下来: 1. 首先,创建自动化帐户, 在资 ...

  10. Autorun.inf文件(2):改变硬盘分区图标

    改变F盘图标. 原理:在f盘下新建一个Autorun.inf文件,文件内容是 [AutoRun]icon=favicon.ico准备名为favicon.ico图标文件,将其放在工程目录里,设计程序将它 ...