Spring AOP: Spring之面向方面编程 拦截器 MethodInterceptor

来源:互联网 发布:景安网络怎么样 编辑:程序博客网 时间:2024/05/17 12:01

1.详解讲解请看:

http://blog.ccidnet.com/blog-htm-do-showone-uid-38235-itemid-96644-type-blog.html


5.1.概念

面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解成各个方面 或者说 关注点 。 这使得可以模块化诸如事务管理等这些横切多个对象的关注点。(这些关注点术语称作 横切关注点。)

Spring的一个关键组件就是AOP框架。 Spring IoC容器(BeanFactory 和ApplicationContext)并不依赖于AOP, 这意味着如果你不需要使用,AOP你可以不用,AOP完善了Spring IoC,使之成为一个有效的中间件解决方案,。

AOP在Spring中的使用:

  • 提供声明式企业服务,特别是作为EJB声明式服务的替代品。这些服务中最重要的是 声明式事务管理,这个服务建立在Spring的事务管理抽象之上。
  • 允许用户实现自定义的方面,用AOP完善他们的OOP的使用。

这样你可以把Spring AOP看作是对Spring的补充,它使得Spring不需要EJB就能提供声明式事务管理;或者 使用Spring AOP框架的全部功能来实现自定义的方面。

如果你只使用通用的声明式服务或者预先打包的声明式中间件服务如pooling,你可以不直接使用 Spring AOP,并且跳过本章的大部分内容.

5.1.1.AOP概念

让我们从定义一些重要的AOP概念开始。这些术语不是Spring特有的。不幸的是,Spring的术语 不是特别地直观。而且,如果Spring使用自己的术语,这将使人更加迷惑。

  • 方面(Aspect): 一个关注点的模块化,这个关注点实现可能 另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的 Advisor或拦截器实现。
  • 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调 用或特定的异常被抛出。
  • 通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类 型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架 包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器 链。
  • 切入点(Pointcut): 指定一个通知将被引发的一系列连接点 的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。
  • 引入(Introduction): 添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。
  • 目标对象(Target Object): 包含连接点的对象。也被称作 被通知被代理对象。
  • AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  • 织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时 完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样, 在运行时完成织入。

各种通知类型包括:

  • Around通知:包围一个连接点的通知,如方法调用。这是最 强大的通知。Aroud通知在方法调用前后完成自定义的行为。它们负责选择继续执行连接点或通过 返回它们自己的返回值或抛出异常来短路执行。(MethodInterceptor就属于此类)
  • Before通知: 在一个连接点之前执行的通知,但这个通知 不能阻止连接点前的执行(除非它抛出一个异常)。
  • Throws通知: 在方法抛出异常时执行的通知。Spring提供 强类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable 或Exception强制类型转换。
  • After returning通知: 在连接点正常完成后执行的通知, 例如,一个方法正常返回,没有抛出异常。

Around通知是最通用的通知类型。大部分基于拦截的AOP框架,如Nanning和JBoss4,只提供 Around通知。

如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需 要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning 通知而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变 得简单,并能减少潜在错误。例如你不需要调用在around通知中所需使用的的MethodInvocation的proceed()方法,因此就调用失败。

切入点的概念是AOP的关键,使AOP区别于其它使用拦截的技术。切入点使通知独立于OO的 层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。 因此切入点构成了AOP的结构要素。

5.1.2.Spring AOP的功能

Spring AOP用纯Java实现。它不需要特别的编译过程。Spring AOP不需要控制类装载器层次, 因此适用于J2EE web容器或应用服务器。

Spring目前支持拦截方法调用。成员变量拦截器没有实现,虽然加入成员变量拦截器支持并不破坏 Spring AOP核心API。

成员变量拦截器在违反OO封装原则方面存在争论。我们不认为这在应用程序开发中是明智的。如 果你需要使用成员变量拦截器,考虑使用AspectJ。

Spring提供代表切入点或各种通知类型的类。Spring使用术语advisor来 表示代表方面的对象,它包含一个通知和一个指定特定连接点的切入点。

各种通知类型有MethodInterceptor (来自AOP联盟的拦截器API)和定义在org.springframework.aop包中的 通知接口。所有通知必须实现org.aopalliance.aop.Advice标签接口。 取出就可使用的通知有 MethodInterceptor、 ThrowsAdvice、 BeforeAdviceAfterReturningAdvice。我们将在下面详细讨论这些通知类型。

Spring实现了AOP联盟的拦截器接口( http://www.sourceforge.net/projects/aopalliance). Around通知必须实现AOP联盟的org.aopalliance.intercept.MethodInterceptor 接口。这个接口的实现可以运行在Spring或其他AOP联盟兼容的实现中。目前JAC实现了AOP联盟的接 口,Nanning和Dynaop可能在2004年早期实现。

Spring实现AOP的途径不同于其他大部分AOP框架。它的目标不是提供及其完善的AOP实现( 虽然Spring AOP非常强大);而是提供一个和Spring IoC紧密整合的AOP实现,帮助解决企业应用 中的常见问题。因此,例如Spring AOP的功能通常是和Spring IoC容器联合使用的。AOP通知是用普通 的bean定义语法来定义的(虽然可以使用\"autoproxying\"功能);通知和切入点本身由Spring IoC 管理:这是一个重要的其他AOP实现的区别。有些事使用Spring AOP是无法容易或高效地实现,比如通知 非常细粒度的对象。这种情况AspectJ可能是最合适的选择。但是,我们的经验是Spring针对J2EE应 用中大部分能用AOP解决的问题提供了一个优秀的解决方案。

5.1.3.Spring中AOP代理

Spring默认使用JDK动态代理实现AOP代理。这使得任何接口或 接口的集合能够被代理。

Spring也可以是CGLIB代理。这可以代理类,而不是接口。如果业务对象没有实现一个接口, CGLIB被默认使用。但是作为一针对接口编程而不是类编程良好实践,业务对象 通常实现一个或多个业务接口。

也可以强制使用CGLIB:我们将在下面讨论,并且会解释为什么你会要这么做。

Spring 1.0后,Spring可能提供额外的AOP代理的类型,包括完全生成的类。这将不会影响 编程模型。

src=\"http://www.jactiongroup.net/reference/s/admons/note.png\"注意控制流切入点是动态切入点中计算代价最高的。Java 1.4中, 它的运行开销是其他动态切入点的5倍。在Java 1.3中则超过10倍。

5.2.4.切入点超类

Spring提供非常实用的切入点的超类帮助你实现你自己的切入点。

因为静态切入点非常实用,你很可能子类化StaticMethodMatcherPointcut,如下所示。 这只需要实现一个抽象方法(虽然可以改写其它的方法来自定义行为)。

class TestStaticPointcut extends StaticMethodMatcherPointcut {    public boolean matches(Method m, Class targetClass) {        // return true if custom criteria match    }}

当然也有动态切入点的超类。

Spring 1.0 RC2或以上版本,自定义切入点可以用于任何类型的通知。

5.2.5.自定义切入点

因为Spring中的切入点是Java类,而不是语言特性(如AspectJ),因此可以定义自定义切入点, 无论静态还是动态。但是,没有直接支持用AspectJ语法书写的复杂的切入点表达式。不过, Spring的自定义切入点也可以任意的复杂。

后续版本的Spring可能象JA一样C提供”语义切入点“的支持:例如,“所有更改目标对象 实例变量的方法”。
<h2 class="title" both\"="" style="margin: 0px; padding: 0px 10px; word-wrap: break-word; font-size: 14px; height: 32px; line-height: 32px; overflow: hidden;">5.3.Spring的通知类型

现在让我们看看Spring AOP是如何处理通知的。

5.3.1.通知的生命周期

Spring的通知可以跨越多个被通知对象共享,或者每个被通知对象有自己的通知。这分别对应 per-classper-instance 通知。

Per-class通知使用最为广泛。它适合于通用的通知,如事务adisor。它们不依赖被代理 的对象的状态,也不添加新的状态。它们仅仅作用于方法和方法的参数。

Per-instance通知适合于导入,来支持混入(mixin)。在这种情况下,通知添加状态到 被代理的对象。

可以在同一个AOP代理中混合使用共享和per-instance通知。

5.3.2.Spring中通知类型

Spring提供几种现成的通知类型并可扩展提供任意的通知类型。让我们看看基本概念和 标准的通知类型。

5.3.2.1.Interception around advice

Spring中最基本的通知类型是interception around advice .

Spring使用方法拦截器的around通知是和AOP联盟接口兼容的。实现around通知的 类需要实现接口MethodInterceptor

public interface MethodInterceptor extends Interceptor {      Object invoke(MethodInvocation invocation) throws Throwable;}

invoke()方法的MethodInvocation 参数暴露将被调用的方法、目标连接点、AOP代理和传递给被调用方法的参数。 invoke()方法应该返回调用的结果:连接点的返回值。

一个简单的MethodInterceptor实现看起来如下:

public class DebugInterceptor implements MethodInterceptor {    public Object invoke(MethodInvocation invocation) throws Throwable {        System.out.println(\"Before: invocation=\");        Object rval = invocation.proceed();        System.out.println(\"Invocation returned\");        return rval;    }}

注意MethodInvocation的proceed()方法的调用。 这个调用会应用到目标连接点的拦截器链中的每一个拦截器。大部分拦截器会调用这个方法,并返回它的返回值。但是, 一个MethodInterceptor,和任何around通知一样,可以返回不同的值或者抛出一个异常,而 不调用proceed方法。但是,没有好的原因你要这么做。

MethodInterceptor提供了和其他AOP联盟的兼容实现的交互能力。这一节下面 要讨论的其他的通知类型实现了AOP公共的概念,但是以Spring特定的方式。虽然使用特定 通知类型有很多优点,但如果你可能需要在其他的AOP框架中使用,请坚持使用MethodInterceptor around通知类型。注意目前切入点不能和其它框架交互操作,并且AOP联盟目前也没有定义切入 点接口。


2.简单小例子

1. 先写出业务对象接口及实现业务对象。

 

Java代码  收藏代码
  1. public interface Interface {  
  2.     public void hello();  
  3. }  

   这里写俩个实现。

Java代码  收藏代码
  1. public class InterfaceImpl implements Interface {  
  2.   
  3.     /**  
  4.      * @see com.alipay.aop.BusinessInterface#hello() 
  5.      */  
  6.     @Override  
  7.     public void hello() {  
  8.         System.out.println("AOP TEST!!");  
  9.     }  
  10. }  
 

 

Java代码  收藏代码
  1. public class InterfaceImplTest implements Interface {  
  2.   
  3.     /**  
  4.      * @see aop.Interface#hello() 
  5.      */  
  6.     @Override  
  7.     public void hello() {  
  8.         System.out.println("helo world!!");  
  9.     }  
  10.   
  11. }  

 

 

2. 实现自己的代理,创建自己的interceptor

 

Java代码  收藏代码
  1. public class MyInterceptor implements MethodInterceptor {  
  2.   
  3.     /**  
  4.      * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) 
  5.      */  
  6.     @Override  
  7.     public Object invoke(MethodInvocation methodInvocation) throws Throwable {  
  8.         String info = methodInvocation.getMethod().getDeclaringClass()+ "." +   
  9.         methodInvocation.getMethod().getName() + "()";  
  10.           
  11.         System.out.println(info);  
  12.           
  13.         try{  
  14.             Object result = methodInvocation.proceed();  
  15.             return result;  
  16.         }  
  17.         finally{  
  18.             System.out.println(info);  
  19.         }  
  20.     }  
  21. }  

 

3. 配置文件

 <?xml version="1.0" encoding="GBK"?>

Java代码  收藏代码
  1. <beans xmlns="http://www.springframework.org/schema/beans"  
  2.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns:p="http://www.springframework.org/schema/p"  
  4.     xmlns:sofa="http://img.alipay.net/dtd/schema/service"  
  5.     xmlns:context="http://www.springframework.org/schema/context"  
  6.     xmlns:webflow="http://www.springframework.org/schema/webflow-config"  
  7.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
  8.          http://img.alipay.net/dtd/schema/service http://img.alipay.net/dtd/schema/service/sofa-service.xsd  
  9.          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd  
  10.          http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"  
  11.     default-autowire="byName">  
  12.       
  13.     <bean id="beanTarget" class="aop.InterfaceImpl"/>  
  14.       
  15.     <bean id="hello" class="aop.InterfaceImplTest" />  
  16.       
  17.     <bean id="myInterceptor" class="aop.MyInterceptor"/>  
  18.       
  19.     <bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean">  
  20.         <property name="proxyInterfaces">  
  21.             <value>aop.Interface</value>  
  22.         </property>  
  23.           
  24.         <property name="interceptorNames">  
  25.             <list>  
  26.                 <value>myInterceptor</value>  
  27.                 <value>beanTarget</value>  
  28.             </list>  
  29.         </property>  
  30.     </bean>  
  31.       
  32.     <bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean">  
  33.         <property name="proxyInterfaces">  
  34.             <value>aop.Interface</value>  
  35.         </property>  
  36.           
  37.         <property name="interceptorNames">  
  38.             <list>  
  39.                 <value>myInterceptor</value>  
  40.                 <value>hello</value>  
  41.             </list>  
  42.         </property>  
  43.     </bean>  
  44. </beans>  
 

 

 

4. 测试代码

 

 

Java代码  收藏代码
  1. public class Main {  
  2.   
  3.     /** 
  4.      *  
  5.      * @param args 
  6.      */  
  7.     public static void main(String[] args) {  
  8.         ClassPathResource resource = new ClassPathResource("spring.xml");  
  9.         XmlBeanFactory beanFactory = new XmlBeanFactory(resource);  
  10.         Interface bean = (Interface)beanFactory.getBean("bean");  
  11.         bean.hello();  
  12.           
  13.         Interface testBean = (Interface)beanFactory.getBean("testBean");  
  14.         testBean.hello();  
  15.     }  
  16. }  
原创粉丝点击