Spring2.0简明手册(系列之四 AOP-2.x)
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://robert.blog.51cto.com/374512/85335 |
从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=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 启用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> 其中的切入点定义,用到了另一个切面(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=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"> <!-- 有了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 本文出自 “点滴积累_勤耕不辍” 博客,请务必保留此出处http://robert.blog.51cto.com/374512/85335 本文出自 51CTO.COM技术博客 |


robert.luo77
博客统计信息
热门文章
最新评论
友情链接