转-Spring Framework中的AOP之around通知
但是如果您希望改变应用程序的常规行为呢?例如说,您希望重写一个方法?这样的话,您就需要使用更积极的around形式的通知。
第一部分的简单例子应用程序包括IbusinessLogic接口、BusinessLogic类和MainApplication类,如下所示:
- public interface IBusinessLogic
- {
- public void foo();
- }
- public class BusinessLogic
- implements IBusinessLogic
- {
- public void foo()
- {
- System.out.println(
- "Inside BusinessLogic.foo()");
- }
- }
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.FileSystemXmlApplicationContext;
- public class MainApplication
- {
- public static void main(String [] args)
- {
- // Read the configuration file
- ApplicationContext ctx =
- new FileSystemXmlApplicationContext(
- "springconfig.xml");
- //Instantiate an object
- IBusinessLogic testObject =
- (IBusinessLogic) ctx.getBean(
- "businesslogicbean");
- // Execute the public
- // method of the bean
- testObject.foo();
- }
- }
要对一个BusinessLogic类的实例彻底重写对foo()方法的调用,需要创建around通知,如下面的AroundAdvice类所示:
- import org.aopalliance.intercept.MethodInvocation;
- import org.aopalliance.intercept.MethodInterceptor;
- public class AroundAdvice
- implements MethodInterceptor
- {
- public Object invoke(
- MethodInvocation invocation)
- throws Throwable
- {
- System.out.println(
- "Hello world! (by " +
- this.getClass().getName() +
- ")");
- return null;
- }
- }
要在spring中用作around通知,AroundAdvice类必须实现MethodInterceptor接口和它的invoke(..)方法。每当截获到方法的重写,invoke(..)方法就会被调用。最后一步是改变包含在应用程序的springconfig.xml文件中的Spring运行时配置,以便可以对应用程序应用AroundAdvice。
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
- "http://www.springframework.org/dtd/spring-beans.dtd">
- <beans>
- <!-- Bean configuration -->
- <bean id="businesslogicbean"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces">
- <value>IBusinessLogic</value>
- </property>
- <property name="target">
- <ref local="beanTarget"/>
- </property>
- <property name="interceptorNames">
- <list>
- <value>theAroundAdvisor</value>
- </list>
- </property>
- </bean>
- <!-- Bean Classes -->
- <bean id="beanTarget"
- class="BusinessLogic"/>
- <!-- Advisor pointcut definition for around advice -->
- <bean id="theAroundAdvisor"
- class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice">
- <ref local="theAroundAdvice"/>
- </property>
- <property name="pattern">
- <value>.*</value>
- </property>
- </bean>
- <!-- Advice classes -->
- <bean id="theAroundAdvice"
- class="AroundAdvice"/>
- </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参数中得到它。
- public class AroundAdvice
- implements MethodInterceptor
- {
- public Object invoke(
- MethodInvocation invocation)
- throws Throwable
- {
- System.out.println(
- "Hello world! (by " +
- this.getClass().getName() +
- ")");
- invocation.proceed();
- System.out.println("Goodbye! (by " +
- this.getClass().getName() +
- ")");
- return null;
- }
- }
图4显示了对proceed()的调用如何影响操作的顺序(与图3所示的初始around通知执行相比较)。
图4. 从around通知内使用proceed()调用原来的方法
当调用proceed()时,实际是在指示被截获的方法(在本例中是foo()方法)利用包含在MethodInvocation对象中的信息运行。您可以通过调用MethodInvocation类中的其他方法来改变该信息。
您可能希望更改包含在MethodInvocation类中的信息,以便在使用proceed()调用被截获的方法之前对被截获方法的参数设置新值。
通过对MethodInvocation对象调用getArguments()方法,然后在返回的数组中设置其中的一个参数对象,最初传递给被截获的方法的参数可以被更改。
如果IbusinessClass和BusinessLogic类的foo()方法被更改为使用整型参数,那么就可以将传递给被截获的调用的值由在AroundAdvice的notify(..)方法中传递改为在foo(int)中传递。
- public class AroundAdvice
- implements MethodInterceptor
- {
- public Object invoke(
- MethodInvocation invocation)
- throws Throwable
- {
- System.out.println(
- "Hello world! (by " +
- this.getClass().getName() +
- ")");
- invocation.getArguments()[0] = new Integer(20);
- invocation.proceed();
- System.out.println(
- "Goodbye! (by " +
- this.getClass().getName() +
- ")");
- return null;
- }
- }
在本例中,被截获的方法的第一个形参被假设为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()方法不是 该功能部件的一部分,所以不进行置换。
- public interface IBusinessLogic
- {
- public void foo();
- }
- public interface IBusinessLogic2
- {
- public void bar();
- public void baz();
- }
此处,ReplacementFeature类扮演了“杜鹃的蛋”的角色,它提供了将被透明地引入应用程序的替代实现。ReplacementFeature类实现了所有在该类引入时要被置换的方法。
- public class ReplacementFeature
- {
- public void foo()
- {
- System.out.println(
- "Inside ReplacementFeature.foo()");
- }
- public void bar()
- {
- System.out.println(
- "Inside ReplacementFeature.bar()");
- }
- }
现在需要声明一个around通知来截获对跨越bean的功能部件的方法调用。CuckoosEgg类提供了某种around通知来检查被截获的方法,并将适当的方法调用传递给ReplacementFeature类的实例。
- public class CuckoosEgg implements MethodInterceptor
- {
- public ReplacementFeature replacementFeature =
- new ReplacementFeature();
- public Object invoke(MethodInvocation invocation)
- throws Throwable
- {
- if (invocation.getMethod().getName().equals("foo"))
- {
- replacementFeature.foo();
- }
- else
- {
- replacementFeature.bar();
- }
- return null;
- }
- }
因为与Spring框架关系密切,Cuckoo's Egg设计的详细信息被放在springconfig.xml配置文件中。对springconfig.xml文件的更改将确保所有对IbusinessLogic和IBusinessLogic2 bean的foo()方法和bar()方法的调用都将被截获,并传递给CuckoosEgg类的around通知。
- ...
- <!--CONFIG-->
- <bean id="businesslogicbean"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces">
- <value>IBusinessLogic</value>
- </property>
- <property name="target">
- <ref local="beanTarget"/>
- </property>
- <property name="interceptorNames">
- <list>
- <value>theCuckoosEggAdvisor</value>
- </list>
- </property>
- </bean>
- <bean id="businesslogicbean2"
- class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces">
- <value>IBusinessLogic2</value>
- </property>
- <property name="target">
- <ref local="beanTarget2"/>
- </property>
- <property name="interceptorNames">
- <list>
- <value>theCuckoosEgg2Advisor</value>
- </list>
- </property>
- </bean>
- <!--CLASS-->
- <bean id="beanTarget" class="BusinessLogic"/>
- <bean id="beanTarget2" class="BusinessLogic2"/>
- <!--ADVISOR-->
- <bean id="theCuckoosEggAdvisor"
- class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice">
- <ref local="theReplacementFeaturePart1Advice"/>
- </property>
- <property name="pattern">
- <value>IBusinessLogic.*</value>
- </property>
- </bean>
- <bean id="theCuckoosEgg2Advisor"
- class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice">
- <ref local="theReplacementFeaturePart2Advice"/>
- </property>
- <property name="pattern">
- <value>IBusinessLogic2.bar*</value>
- </property>
- </bean>
- <!--ADVICE-->
- <bean id="theReplacementFeaturePart1Advice" class="CuckoosEgg"/>
- <bean id="theReplacementFeaturePart2Advice" class="CuckoosEgg"/>
- ...
当使用修改后的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通知的更多相关文章
- Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->关于spring framework中的beans
Spring framework中的beans 1.概述 bean其实就是各个类实例化后的对象,即objects spring framework的IOC容器所管理的基本单元就是bean spring ...
- Spring Framework中常见的事务传播陷阱(译文)
最近看到Medium上一篇讨论Spring Framework中事务传播的文章,解释了几种常见的问题,解释的不错,这里直接翻译吧(意译为主,粗体和斜体是我自己加上的). 译文: 这是我的第一篇文章,我 ...
- 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架构模式,可 ...
- Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework中web相关的知识(概述)
Spring Framework中web相关的知识 1.概述: 参考资料:官网documentation中第22小节内容 关于spring web mvc: spring framework中拥有自 ...
- Spring Boot中使用AOP统一处理Web请求日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...
- Spring Boot中使用AOP记录请求日志
这周看别人写的springboot后端代码中有使用AOP记录请求日志,以前没接触过,因此学习下. 一.AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编 ...
- 46. Spring Boot中使用AOP统一处理Web请求日志
在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...
- (转)Spring Boot中使用AOP统一处理Web请求日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...
- spring中的AOP 以及各种通知 配置
理解了前面动态代理对象的原理之后,其实还是有很多不足之处,因为如果在项目中有20多个类,每个类有100多个方法都需要判断是不是要开事务,那么方法调用那里会相当麻烦. spring中的AOP很好地解决了 ...
随机推荐
- IOS中的编码规范
1.指导原则 [原则1-]首先是为人编写程序,其次才是计算机. 说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发.测试.生产.用户使用.版本升级和后期维护等长期过程,只有易读.易维护的软件代 ...
- iOS学习之下拉刷新、上拉加载
http://blog.csdn.net/mx_xuanxiao/article/details/50595370
- 设置Ubuntu为本地时间
大家在切换操作系统的时候会发现一个问题, Windows 和 Ubuntu 的时间会出现不一致的情况.在 Windows 中把时间设置正确了过后,回到在 Ubuntu 后系统的时间又不一样了,在 Ub ...
- JS总结 运算符 条件语句
算术运算符+-*/ 与数学计算一致,配合()号进行各种计算 另外,+号还可以达到连接的作用.例如: var a = 15; alert("a的值等于"+a); 前后增减量运算符: ...
- oracle之rownum(伪列)
整理和学习了一下网上高手关于rownum的帖子: 参考资料: http://tech.ddvip.com/2008-10/122490439383296.html 和 http://tenn.jav ...
- vi学习 常用命令-新建-复制-剪切-粘贴
mkdir /home/brandon.du/desktop/mylinux/test_1.txt ---------mkdir新建文件夹 rm /home/brandon.du/desktop/ ...
- Cisco ASA使用证书加密
使用ASDM配置HTTPS证书加密anyconnect连接 一.在没有使用证书的情况下每次连接VPN都会出现如下提示 ASA Version: 8.4.(1) ASDM Version: 6.4.(7 ...
- wamp apache 的虚拟机配置 多域名访问 的 三部曲
wamp apache 的虚拟机配置 多域名访问 的 三部曲 wamp: 1:C:\WINDOWS\system32\drivers\etc->hosts 加入自己的 ...
- [转帖]零投入用panabit享受万元流控设备——搭建篇
原帖地址:http://net.it168.com/a2009/0505/274/000000274918.shtml 你想合理高效的管理内网流量吗?你想针对各个非法网络应用与服务进行合理限制吗?你是 ...
- [转]const 与 readonly知多少
引自:http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你const与r ...