在Spring AOP 组件中,拦截器分为两种类型,即 Advice 和 Advisor。
Advice 有四种类型: 前置通知(before advice ) ; 后置通知(after advice ) ; 环绕通知(around advice);异常通知(exception advice)。
Spring AOP编程的核心是通过 IoC生成代理对象,代理对象的配置通常有如下属性:
<bean id="customersServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interfaces"> <list><value>bookone.service.CustomerService</value></list></property>
<property name="targetName"><value>customerServiceImpl</value></property>
<property name="interceptorNames">
<list>
<value>logbefore</value>
<value>logafter</value>
<value>logaround</value>
<value>logthrows</value>
</list>
</property>
</bean>
其中interfaces是目标对象所实现的接口,targetName是目标对象的名字,interceptorNames是Advice和Advisor。
- 前置通知(before advice)
前置通知需要实现MethodBeforeAdvice接口,并覆盖其方法:void before(Method method,Object[] args,Object target).该方法在目标对象的方法调用前被调用。其中method是被调用的方法,args是该方法的参数,target是被调用的实例。前置通知不会阻拦目标对象方法的执行,除非前置通知中抛出了未处理的异常,否则前置通知执行后,总会执行目标对象的方法。使用前置通知,需要创建前置通知类,实现methodBeforeAdvice接口,代码如下:
public class LogBeforeAdvice implements MethodBeforeAdvice {
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("Invoke LogBeforeAdvice.before");
Logger.log(arg0.getName()); //调用了Logger类的log方法,实现了日志功能。
}
}
- 后置通知(after advice)
与前置通知相对应的,Spring AOP组件提供了后置通知类型。后置通知在一个方法被调用后开始执行。创建后置通知的实现类必须先实现接口AfterReturningAdvice,并覆盖其方法:void afterReturning(Object returnValue,Method method,Object[] args,Object target).
afterReturning方法与前置接口的before方法类似,只是多了一个返回值参数returnValue。在afterReturning方法中能访问被拦截方法的返回值,但是不能修改这个返回值。
后置通知的实现类代码如下:
public class LogAfterAdvice implements AfterReturningAdvice {
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("Invoke LogAfterAdvice.afterReturning");
Logger.log("LogAfterAdvice : " + arg1.getName() +"return"+arg0);
}
}
重写了afterReturning方法,该方法将在被拦截方法被调用后执行,将被拦截方法的名字和返回值写到日志文件里。
- 环绕通知(around advice)
环绕通知可以在被拦截的方法执行过程的任何一个阶段被执行,也可能由于某种原因,而不调用被拦截的方法。这是前置和后置通知所做不到的。前置和后置通知无法阻止被拦截的方法被调用。环绕通知必须实现MethodInterceptor接口,覆盖接口中的方法: object invoke(MethodInvocation invocation)。
invoke方法的参数类型是MethodInvocation,该参数类型提供了proceed方法,用来调用被拦截的方法,如果环绕通知中没有调用proceed方法,那么目标方法将不会被调用。环绕通知的实现类如下:
public class LogAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation arg0) throws Throwable {
Method method = arg0.getMethod();
if(!method.getName().equals("viewAll")){
System.out.println("Invoke LogAroundAdvice.invoke");
Logger.log("LogAroundAdvice :" + method.getName());
return arg0.proceed();
}else{
return null;
}
}
}
上述代码中,在invoke方法中首先通过调用MethodInvocation的getMethod方法返回被拦截的方法,再通过getName方法得到被拦截方法的名字。如果这个方法的名字不是“viewAll”,那么就在日志文件中记录方法名字,并通过proceed调用执行该方法。;而如果方法的名字是“viewAll”,那么它将被拦截,不被调用,也就是说环绕通知阻止了被拦截方法的调用。
4.异常通知(Exception advice)
异常通知在被拦截的方法抛出异常时调用。异常通知必须实现ThrowsAdvice接口。该接口中没有方法,是一个标记接口。实现该接口后,异常通知可以定义多个方法,方法必须符合如下规范: afterThrowing([Method], [args], [target], Throwable subclass) 。其中[Method], [args], [target]都是可选项,只有Throwable是必须指定的,即抛出异常的具体类型必须指定。异常通知的实现类如下所示:
public class LogThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException e){
System.out.println("Invoke LogThrowsAdvice.afterThrowing");
Logger.log("LogThrowsAdvice : "+ e.getMessage());
}
public void afterThrowing(IOException e){
Logger.log(e.getMessage());
}
}
上述异常通知中定义了两个方法,方法中指定了不同异常类型的形式参数,分别处理RemoteException和IOException 。当被拦截的方法抛出了对应类型的异常后,将被异常通知类拦截。
这就是Spring框架中AOP组件的四种常用的Advice类型。