实现MethodInterceptor 接口,在调用目标对象的方法时,就可以实现在调用方法之前、调用方法过程中、调用方法之后对其进行控制。
MethodInterceptor 接口可以实现MethodBeforeAdvice接口、AfterReturningAdvice接口、ThrowsAdvice接口这三个接口能够所能够实现的功能,但是应该谨慎使用MethodInterceptor 接口,很可能因为一时的疏忽忘记最重要的MethodInvocation而造成对目标对象方法调用失效,或者不能达到预期的设想。
关于含有Advice的三种对目标对象的方法的增强,可以参考文章在Spring的IOC容器中装配AOP代理 。
在在Spring的IOC容器中装配AOP代理 的基础上,比较MethodInterceptor 接口的实现与上面提及到的三种接口实现对目标对象方法的增强的功能效果。
我们将从应用中分离出日志切面,,将对日志的操作整合到实现MethodInterceptor 接口的类SpringMethodInterceptor中,该实现类的代码如下所示:
package org.shirdrn.spring.aop;
import java.util.Date;
import org.aopalliance.intercept.MethodInterceptor ;
import org.aopalliance.intercept.MethodInvocation;
public class SpringMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invo) throws Throwable {
Object[] object = invo.getArguments();
try{
String date1 = (new Date()).toLocaleString();
System.out.println("信息:[MethodInterceptor ]["+date1+"]用户 "+object[0]+" 正在尝试登录陆系统...");
Object returnObject = invo.proceed();
String date2 = (new Date()).toLocaleString();
System.out.println("信息:[MethodInterceptor ]["+date2+"]用户 "+object[0]+" 成功登录系统.");
return returnObject;
}
catch(Throwable throwable){
if(object[0].equals("Jessery")){
throw new Exception("信息:[MethodInterceptor ]不允许黑名单中用户 "+object[0]+" 登录系统");
}
}
return object;
}
}
程序中,红色标示的代码行Object returnObject = invo.proceed();很关键,只有通过它来对目标对象方法调用,返回一个Object对象。
调用目标对象方法之前,可以添加跟踪日志,对方法增强,相当于使用MethodBeforeAdvice接口对方法进行增强。
调用目标对象方法之后,也可以添加跟踪日志,对方法增强,相当于使用AfterReturningAdvice接口对方法进行增强。
在执行目标对象方法的过程中,如果发生异常,可以在catch中捕获异常,相当于使用ThrowsAdvice接口对方法进行增强。
上面实现了AOP,同时要在XML中装配,配置如下所示:
<bean id="springMethodInterceptor"
class="org.shirdrn.spring.aop.SpringMethodInterceptor"
abstract="false" singleton="true" lazy-init="default"
autowire="default" dependency-check="default">
</bean>
<bean id="accountService"
class="org.springframework.aop.framework.ProxyFactoryBean"
abstract="false" singleton="true" lazy-init="default"
autowire="default" dependency-check="default">
<property name="target">
<ref bean="accountServiceImpl" />
</property>
<property name="interceptorNames">
<list>
<value>loginMethodBeforeAdvice</value>
<value>loginAfterReturningAdvice</value>
<value>loginThrowsAdvice</value>
<value>springMethodInterceptor</value>
</list>
</property>
</bean>
红色标示部分为对使用MethodInterceptor 对目标对象方法进行增强的配置。
测试主函数同文章在Spring的IOC容器中装配AOP代理 中的相同,如下所示:
package org.shirdrn.main;
import org.shirdrn.interf.AccountServiceInterf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
String name = "shirdrn";
String pwd = "830119";
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountServiceInterf asi = (AccountServiceInterf)ctx.getBean("accountService");
asi.login(name, pwd);
}
}
运行输出结果如下所示:
信息:[2008-3-23 17:39:38]用户 shirdrn 正在尝试登录陆系统...
信息:[MethodInterceptor ][2008-3-23 17:39:39]用户 shirdrn 正在尝试登录陆系统...
信息:[MethodInterceptor ][2008-3-23 17:39:42]用户 shirdrn 成功登录系统.
信息:[2008-3-23 17:39:42]用户 shirdrn 成功登录系统.
可见,标示为[MethodInterceptor ]的输出信息,就是MethodInterceptor 对调用目标对象方法的增强的结果。
如果我们使用非法的用户帐户登录系统:
String name = "Jessery";
String pwd = "jessery";
就会被MethodInterceptor 拦截器拦截,而且抛出异常,如下所示:
信息:[2008-3-23 17:52:18]用户 Jessery 正在尝试登录陆系统...
信息:[MethodInterceptor ][2008-3-23 17:52:18]用户 Jessery 正在尝试登录陆系统...
log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory).
log4j:WARN Please initialize the log4j system properly.
信息:[MethodInterceptor ][2008-3-23 17:52:24]用户 Jessery 登录失败.
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at org.shirdrn.impl.AccountServiceImpl$$EnhancerByCGLIB$$32dd7c51.login(<generated>)
at org.shirdrn.main.Main.main(Main.java:16)
用户登录过程中发生异常: Exception
Caused by: java.lang.Exception: 信息:[MethodInterceptor ]不允许黑名单中用户 Jessery 登录系统.
at org.shirdrn.spring.aop.SpringMethodInterceptor.invoke(SpringMethodInterceptor.java:27)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:623)
... 2 more
我们可以看到,使用MethodInterceptor 拦截器打印出了三项相关信息:
调用目标对象的方法之前,对其进行了增强:
信息:[MethodInterceptor ][2008-3-23 17:52:18]用户 Jessery 正在尝试登录陆系统...
因为不允许非法用户Jessery登录系统,即不允许Jessery调用login方法,故在调用login方法过程中抛出了异常,并且进行了日志跟踪:
信息:[MethodInterceptor ][2008-3-23 17:52:24]用户 Jessery 登录失败.
Caused by: java.lang.Exception: 信息:[MethodInterceptor ]不允许黑名单中用户 Jessery 登录系统.
总结:
使用Spring的Bean装配AOP,对于MethodBeforeAdvice接口、AfterReturningAdvice接口、ThrowsAdvice接口这三个接口在XML配置文件中配置的顺序对调用目标对象的方法没有关系。
但是如果在使用上述的基础上又使用了MethodInterceptor ,如果MethodInterceptor 配置顺序不同,就可能将对目标对象方法的调用进行拦截,使得我们预期设想的使用AfterReturningAdvice对方法调用之后增强失效。
因此,如果两类Advice同时使用,在装配的时候,在XML配置文件中,将MethodInterceptor 的配置放在其他三种Advice的后面,使得前三种Advice先起作用,最后使用MethodInterceptor 进行拦截。