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