Spring2.0简明手册(系列之四 AOP-2.x)

从Spring2.0开始,Spring对AOP的支持增强了。使用起来更加方便,更加强大。
下面我们来看看新增的两种使用方式:@AspectJ风格和XML(schema)风格。
1.@AspectJ风格
首先通过实例来展示一下用法:
先看一下配置文件(aop_config_Annotation.xml):
<? xml version ="1.0" encoding ="UTF-8" ?>
< beans xmlns ="http://www.springframework.org/schema/beans"
           xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >

<!--
启用Spring对@AspectJ的支持:
    需要在你的应用程序的classpath中引入两个AspectJ库:aspectjweaver.jar 和 aspectjrt.jar。
    这些库可以在AspectJ的安装包(1.5.1或者之后的版本)中的 lib 目录里找到,
    或者也可以在Spring依赖库的 lib/aspectj 目录下找到。
另外,如果想强制使用CGLIB代理,需要将 <aop:aspectj-autoproxy> 的 proxy-target-class 属性设为true。
-->
< aop:aspectj-autoproxy />

<!--
这个bean指向一个切面(使用了@Aspect注解的bean类)
在启用@AspectJ支持的情况下,在application context中定义的
任意带有一个@Aspect切面(拥有@Aspect注解)的bean都将被Spring自动识别并配置在Spring AOP。
-->
< bean id ="aAspectBean" class ="com.xyz.myapp.AspectExample" >
  ...
</ bean >
<!--
这个bean只是一个普通的bean(没有使用@Aspect注解),
只要符合切面中的切入点,就会被SpringAOP自动代理
-->
< bean id ="aTargetBean" class ="com.xyz.myapp.service.impl.AnImpledService" >
  ...
</ bean >
</ beans >
接下来是我们定义的切面的具体实现(AspectExample.java),
其中的切入点定义,用到了另一个切面(SystemArchitecture.java)
AspectExample.java:
package com.xyz.myapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;


@Aspect
//这个类使用了org.aspectj.lang.annotation.Aspect 注解,这个类被视作一个@Aspect切面
public class AspectExample {
   //切面可能包括切入点,通知和引入(inter-type)声明。
   //可以在同一个切面里定义多个通知,或者其他成员。
   //前置通知(Before advice)--在一个匹配的方法执行前执行
  @Before( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
   public void doAccessCheck() {
     // ...
  }
   //使用一个in-place 的切入点表达式
  @Before( "execution(* com.xyz.myapp.dao.*.*(..))")
   public void doAccessCheck() {
     // ...
  }

   //返回后通知(After returning advice)--在一个匹配的方法返回的时候执行
  @AfterReturning( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
   public void doAccessCheck() {
     // ...
  }
   //为了在通知体内得到返回的值使用了returning 属性
   //在 returning 属性中使用的名字必须对应于通知方法内的一个参数名。
   //当一个方法执行返回后,返回值作为相应的参数值传入通知方法。
   //returning 子句也限制了匹配的返回值类型
   //(在本例子中,返回值是 Object 类,也就是说返回任意类型都会匹配)
  @AfterReturning(
    pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    returning= "retVal")
   public void doAccessCheck(Object retVal) {
     // ...
  }

   //抛出后通知(After throwing advice)--在一个方法抛出异常后执行
  @AfterThrowing( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
   public void doRecoveryActions() {
     // ...
  }
   //限制通知只在某种特殊的异常被抛出的时候匹配,并可以在通知体内得到被抛出的异常。
   //使用 throwing 属性不光可以限制匹配的异常类型(如果你不想限制,请使用 Throwable 作为异常类型),
   //还可以将抛出的异常绑定到通知的一个参数上。
   //在 throwing 属性中使用的名字必须与通知方法内的一个参数对应。 当一个方法因抛出一个异常而中止后,
   //这个异常将会作为那个对应的参数送至通知方法。
   //throwing 子句也限制了只能匹配到抛出指定异常类型的方法(在本例子中为 DataAccessException)。
  @AfterThrowing(
    pointcut= "com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    throwing= "ex")
   public void doRecoveryActions(DataAccessException ex) {
     // ...
  }

   //后通知(After (finally) advice)
   //不论一个方法是如何结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。
   //这个通知必须做好处理正常返回和异常返回两种情况。通常用来释放资源。
  @After( "com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
   public void doReleaseLock() {
     // ...
  }

   //环绕通知(Around Advice)
   //环绕通知在一个方法执行之前和之后执行。 它使得通知有机会既在一个方法执行之前又在执行之后运行。
   //并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
   //环绕通知经常在在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。
  @Around( "com.xyz.myapp.SystemArchitecture.businessService()")
   //通知的第一个参数必须是 ProceedingJoinPoint 类型。
   public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
     // start stopwatch
     //在通知体内,调用 ProceedingJoinPoint 的 proceed() 方法将会导致潜在的连接点方法执行。
     //proceed 方法也可能会被调用并且传入一个 Object[] 对象-该数组将作为方法执行时候的参数。
     //请注意proceed可能在通知体内部被调用一次,许多次,或者根本不被调用。
    Object retVal = pjp.proceed();
     // stop stopwatch
     //方法的调用者得到的返回值就是环绕通知返回的值。
     return retVal;
  }
}
SystemArchitecture.java:
package com.xyz.myapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
//这个切面中定义的切入点,可以在任何需要切入点表达式的地方引用
public class SystemArchitecture {

   /**
   * A join point is in the web layer if the method is defined
   * in a type in the com.xyz.myapp.web package or any sub-package
   * under that.
   */
  @Pointcut( "within(com.xyz.myapp.web..*)") // the pointcut expression
   public void inWebLayer() {} // the pointcut signature

   /**
   * A join point is in the service layer if the method is defined
   * in a type in the com.xyz.myapp.service package or any sub-package
   * under that.
   */
  @Pointcut( "within(com.xyz.myapp.service..*)")
   public void inServiceLayer() {}

   /**
   * A join point is in the data access layer if the method is defined
   * in a type in the com.xyz.myapp.dao package or any sub-package
   * under that.
   */
  @Pointcut( "within(com.xyz.myapp.dao..*)")
   public void inDataAccessLayer() {}

   /**
   * A business service is the execution of any method defined on a service
   * interface. This definition assumes that interfaces are placed in the
   * "service" package, and that implementation types are in sub-packages.
   *
   * If you group service interfaces by functional area (for example,
   * in packages com.xyz.myapp.abc.service and com.xyz.def.service) then
   * the pointcut expression "execution(* com.xyz.myapp..service.*.*(..))"
   * could be used instead.
   */
  @Pointcut( "execution(* com.xyz.myapp.service.*.*(..))")
   public void businessService() {}

   /**
   * A data access operation is the execution of any method defined on a
   * dao interface. This definition assumes that interfaces are placed in the
   * "dao" package, and that implementation types are in sub-packages.
   */
  @Pointcut( "execution(* com.xyz.myapp.dao.*.*(..))")
   public void dataAccessOperation() {}

}
@AspectJ风格的基本使用就如上面例子所示。
下面进行一些补充的说明:
切入点表达式:
  这个很重要,为了灵活的设置我们的切面插入的位置,必须对它有足够的认识。
 使用了AspectJ 切入点表达式语言。
  JDK1.5及以上版本支持命名式切入点,使用命名切入点能够明显的提高代码的可读性。
  更多信息参见:Spring参考手册--6.2.3. 声明一个切入点(pointcut)
 切入点指定者的支持
  其中最主要的切入点指定者是execution
 合并切入点表达式
  从更小的命名组件来构建更加复杂的切入点表达式是一种最佳实践。
 共享常见的切入点(pointcut)定义
 切入点表达式示例
通知参数(Advice parameters)
  为了在通知(adivce)体内访问参数,需要进行参数绑定。
 更多信息参见:Spring参考手册--6.2.4.6. 通知参数(Advice parameters)
通知(Advice)顺序
  通过让切面的支持bean实现Ordered接口来指定优先级,进而控制通知的执行顺序。
 更多信息参见:Spring参考手册--6.2.4.7. 通知(Advice)顺序
引入(Introductions)
  为了让被通知对象实现一个给定的接口,应该使用引入。
  更多信息参见:Spring参考手册--6.2.5. 引入(Introductions)
高级主题:切面实例化模型
 默认情况下,在application context中每一个切面都会有一个实例。
  AspectJ 把这个叫做单个实例化模型(singleton instantiation model)。
 Spring还支持AspectJ的 perthis 和 pertarget 实例化模型。
  更多信息参见:Spring参考手册--6.2.6. 切面实例化模型
-----------------------------------------------------------
2.基于schema的风格
先看一下配置文件(aop_config_schema.xml):
<? xml version ="1.0" encoding ="UTF-8" ?>
< beans xmlns ="http://www.springframework.org/schema/beans"
           xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:aop="http://www.springframework.org/schema/aop"  
        xsi:schemaLocation="
[url]http://www.springframework.org/schema/beans[/url]
[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]
[url]http://www.springframework.org/schema/aop[/url]
[url]http://www.springframework.org/schema/aop/spring-aop-2.0.xsd[/url]" >
<!--
有了schema的支持,切面就和常规的Java对象一样被定义成application context中的一个bean。
对象的字段和方法提供了状态和行为信息,XML文件则提供了切入点和通知信息。
正如下面这个bean指向一个没有使用 @Aspect 注解的bean类,
但是这个类会在下面被配置为一个切面的backing bean(支持bean)。
-->
< bean id ="aBean" class ="com.xyz.myapp.AspectExample2" >
  ...
</ bean >
<!--
配置文件中:
所有的AOP配置是在<aop:config>标签中设置的,所有的切面和通知器都必须定义在 <aop:config> 元素内部。
一个application context可以包含多个 <aop:config>。
一个 <aop:config> 可以包含pointcut,advisor和aspect元素(注意它们必须按照这样的顺序进行声明)。
如果想强制使用CGLIB代理,需要将 <aop:config> 的 proxy-target-class 属性设为true
-->
< aop:config >
  <!-- 顶级(<aop:config>)切入点:
  直接在<aop:config>下定义,这样就可以使多个切面和通知器共享该切入点。-->
   < aop:pointcut id ="businessService"
   expression ="execution(* com.xyz.myapp.service.*.*(..))" />
  <!-- 这里使用命名式切入点,只在JDK1.5及以上版本中支持。-->
   < aop:pointcut id ="businessService"
   expression ="com.xyz.myapp.SystemArchitecture.businessService()" />

  <!-- 切面使用<aop:aspect>来声明,backing bean(支持bean)通过 ref 属性来引用-->
   < aop:aspect id ="myAspect" ref ="aBean" >
    <!-- 在切面里面声明一个切入点:这种情况下切入点只在切面内部可见。-->
     < aop:pointcut id ="businessService"
     expression ="execution(* com.xyz.myapp.service.*.*(..))" />

    <!-- Before通知-->
     < aop:before
   pointcut-ref ="dataAccessOperation"
   method ="doAccessCheck" />
    <!-- 使用内置切入点:将 pointcut-ref 属性替换为 pointcut 属性-->
     < aop:before
   pointcut ="execution(* com.xyz.myapp.dao.*.*(..))"
   method ="doAccessCheck" />

    <!-- 返回后通知-->
     < aop:after-returning
   pointcut-ref ="dataAccessOperation"
   method ="doAccessCheck" />
    <!-- 和@AspectJ风格一样,通知主体可以接收返回值。使用returning属性来指定接收返回值的参数名-->
     < aop:after-returning
   pointcut-ref ="dataAccessOperation"
   returning ="retVal"
   method ="doAccessCheck" />

    <!-- 抛出异常后通知-->
     < aop:after-throwing
   pointcut-ref ="dataAccessOperation"
   method ="doRecoveryActions" />
    <!-- 和@AspectJ风格一样,可以从通知体中获取抛出的异常。
    使用throwing属性来指定异常的名称,用这个名称来获取异常-->
     < aop:after-throwing
   pointcut-ref ="dataAccessOperation"
   thowing ="dataAccessEx"
   method ="doRecoveryActions" />

    <!-- 后通知-->
     < aop:after
   pointcut-ref ="dataAccessOperation"
   method ="doReleaseLock" />

    <!-- Around通知:通知方法的第一个参数的类型必须是 ProceedingJoinPoint 类型-->
     < aop:around
   pointcut-ref ="businessService"
   method ="doBasicProfiling" />
   </ aop:aspect >
</ aop:config >
</ beans >
其中,切面的支持bean(AspectExample2)这个类跟我们在@AspectJ的例子中使用的AspectExample是相同的,
只是没有使用注解。切入点和通知信息通过XML配置来提供。
schema风格的基本使用就如上面例子所示。
下面进行一些补充的说明:
通知参数(Advice parameters) 
 参见:Spring参考手册6.3.3.6. 通知参数
通知(Advice)顺序 
 切面的优先级通过切面的支持bean是否实现了Ordered接口来决定。
引入(Introductions) 
 参见:Spring参考手册6.3.4. 引入
切面实例化模型 
 Schema-defined切面仅支持一种实例化模型就是singlton模型。
Advisors 
 参见:Spring参考手册6.3.6. Advisors

你可能感兴趣的:(spring,职场,休闲,手册,简明)