前言:
的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别:
1) 目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。
2) 环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用
这里是经过我自己测试的过的例子,使用面向切面来处理一些问公共的问题,比如,权限管理,事务的委托
下面的例子就是使用环绕通知,当程序发生异常时,重复提交请求,重复的次数是可以设定的
当我们开发企业级应用时,通常会想要从几个切面来引用模块化的应用和特定操作的集合,下面是一个典型的通用切面,看起来可能像下面这样(这也是Spring文档里的)
package test.prefer.aspect;
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.someapp.web package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.web..*)") public void inWebLayer() {}/**
* A join point is in the service layer if the method is defined * in a type in the com.xyz.someapp.service package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.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.someapp.dao package or any sub-package * under that. */ @Pointcut("within(com.xyz.someapp.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.someapp.abc.service and com.xyz.def.service) then * the pointcut expression "execution(* com.xyz.someapp..service.*.*(..))" * could be used instead. * * Alternatively, you can write the expression using the 'bean' * PCD, like so "bean(*Service)". (This assumes that you have * named your Spring service beans in a consistent fashion.) */ @Pointcut("execution(* test.prefer.aspect.*.*(..))") 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.someapp.dao.*.*(..))") public void dataAccessOperation(){}}
一、定义自己的一个切面
/*
*文件名:ConcurrentOperationExecutor.*描述:<描述>*修改人:Administrator*/package test.prefer.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.core.Ordered;/**
* @author 2010-6-1 */@Aspectpublic class ConcurrentOperationExecutor implements Ordered { private static final int DEFAULT_MAX_RETRIES = 2;private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries; } public int getOrder(){ return this.order; } public void setOrder(int order){ this.order = order; } @Around("test.prefer.aspect.SystemArchitecture.businessService()") public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { //环绕通知处理方法 int numAttempts = 0; Exception lockFailureException; do { numAttempts++; try { System.out.println("环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ]............."); return pjp.proceed(); } catch(Exception ex) { lockFailureException = ex; } } while(numAttempts <= this.maxRetries); throw lockFailureException; }}
说明:
请注意切面实现了 Ordered
接口,这样我们就可以把切面的优先级设定为高于事务通知 (我们每次重试的时候都想要在一个全新的事务中进行)。maxRetries
和order
属性都可以在Spring中配置。主要的动作在doConcurrentOperation
这个环绕通知方法中发生。 请注意这个时候我们所有的businessService()
方法都会使用这个重试策略。 我们首先会尝试处理,如果得到一个Exception
异常, 我们仅仅重试直到耗尽所有预设的重试次数(spring开发文档)
二、在配置文件里配置这个切面
<aop:aspectj-autoproxy/>
<bean id="concurrentOperationExecutor" class="test.prefer.aspect.ConcurrentOperationExecutor"> <property name="maxRetries" value="3"/> <property name="order" value="100"/> </bean>
好了,下面我们就试一下效果吧
三、测试效果
1)新建一个测试的bean: MyTestAspect,代码如下:
package test.prefer.aspect;
/** * 这是一个切面类 */import org.aspectj.lang.annotation.Aspect;public class MyTestAspect {
int k=0; public void test(String args) throws Exception{ System.out.println("这里是[ 目标 ]方法test()"+ ++k); if(k<2){ throw new Exception(); } }}
这个类必须在连接点的包或者子包下面,
在SystemArchitecture里有定义
@Pointcut("execution(* test.prefer.aspect.*.*(..))")
public void businessService(){}
2)applicationContext.xml里配置 MyTestAspect
<bean id="test" class="test.prefer.aspect.MyTestAspect"/>
3)好了,看看效果吧
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import test.prefer.aspect.MyTestAspect;public class example {
public static void main(String args[]){ ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); MyTestAspect t =(MyTestAspect)ctx.getBean("test"); try{ t.test(""); }catch(Exception e){ System.out.println("main()中处理异常"+e); } }}
输出结果是:
环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............
这里是[ 目标 ]方法test()1环绕通知方法[ doConcurrentOperation(ProceedingJoinPoint pjp) ].............这里是[ 目标 ]方法test()