上一篇日志使用的前置通知,后置通知,环绕通知和异常通知可以很好的拦截方法,并插入相应的代码。但是却存在一个问题,就是Spring AOP拦截了目标类中的所有方法。而在通常情况下,只需要拦截部分方法,比如只需要拦截目标类中方法名以set开始的所有方法。Spring AOP提供了Advisor和PointcutAdvisor来达到这个目的。
常用的PointAdvisor有三种,DefaultPointcutAdvisor , RegexpMethodPointcutAdvisor 和 NameMatchMethodPointcutAdvisor,它们都在org.springframework.aop.support包中。由它们的名字可以看出,RegexpMethodPointcutAdvisor是通过正则表达式来匹配拦截的方法,NameMatchMethodPointcutAdvisor通过直接指定那些方法是需要拦截的,它也可以用*作为通配符。DefaultPointcutAdvisor则需要自定义切入点类。
在上一篇日志的工程基础上的示例工程代码:
结构没多少差别,就是com.aop包中多了一个MyPointcut类。当然applicationContext.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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="myUserService" class="com.service.UserService"></bean> <!-- 定义前置通知 --> <bean id="beforeLogAdvice" class="com.aop.BeforeLogAdvice"></bean> <bean id="beforePointcut" class="com.aop.MyPointcut"></bean> <bean id="myAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut"> <ref local="beforePointcut"/> </property> <property name="advice"> <ref local="beforeLogAdvice"/> </property> </bean> <!-- 定义后置通知 --> <bean id="afterLogAdvice" class="com.aop.AfterLogAdvice"></bean> <bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*deleteUser</value> </list> </property> <property name="advice"> <ref local="afterLogAdvice"/> </property> </bean> <!-- 定义环绕通知 --> <bean id="logAroundAdvice" class="com.aop.LogAroundAdvice"></bean> <bean id="myAroundAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedNames"> <list> <value>*User</value> </list> </property> <property name="advice"> <ref local="logAroundAdvice" /> </property> </bean> <!-- 定义异常通知 --> <bean id="throwsLogAdvice" class="com.aop.ThrowsLogAdvice"></bean> <!-- 定义代理类,名 称为myProxy,将通过myProxy访问业务类中的方法 --> <bean id="myProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.service.IUserService</value> </property> <property name="interceptorNames"> <list> <value>myAdvisor</value> <value>regexpAdvisor</value> <value>myAroundAdvisor</value> <value>throwsLogAdvice</value> </list> </property> <property name="target" ref="myUserService"></property> </bean> </beans>
对比上一个工程applicationContext.xml文件的内容,id为myProxy的bean里的interceptorNames拦截器名的list下不再是四个通知了。前置通知,后置通知和环绕通知都变成了Advisor。下面挨个来看。
首先看前置通知:
<!-- 定义前置通知 --> <bean id="beforeLogAdvice" class="com.aop.BeforeLogAdvice"></bean> <bean id="beforePointcut" class="com.aop.MyPointcut"></bean> <bean id="myAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut"> <ref local="beforePointcut"/> </property> <property name="advice"> <ref local="beforeLogAdvice"/> </property> </bean>
这里自定义了一个MyPointcut切入点类:
package com.aop; import java.lang.reflect.Method; import org.springframework.aop.support.NameMatchMethodPointcut; public class MyPointcut extends NameMatchMethodPointcut { public boolean matches(Method method, Class targetClass) { this.setMappedName("addUser"); //this.setMappedName("*User"); return super.matches(method, targetClass); } }
Spring AOP在调动目标类的每一个方法之前,都会调用这里的matches方法来判断是否应该拦截该方法,当matches方法返回true时,表明当前方法应该被拦截。如果要想指定被拦截的方法,需要调用NameMatchMethodPointcut的setMappedName方法。本例中指定了addUser方法。当然也可以使用*作为通配符。如代码中的注释,this.setMappedName("*User")将会拦截以User结尾的所有方法。
要想使用自定义的这个Pointcut类,必须装配DefaultPointcutAdvisor,即applicationContext.xml中id为myAdvisor的bean。这个bean中的pointcut属性指定了切入点,advice属性指定了通知。
再来看看后置通知:
<!-- 定义后置通知 --> <bean id="afterLogAdvice" class="com.aop.AfterLogAdvice"></bean> <bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*deleteUser</value> </list> </property> <property name="advice"> <ref local="afterLogAdvice"/> </property> </bean>
这里用到了RegexpMethodPointcutAdvisor。bean下的patterns属性下添加一些正则表达式规则,能够匹配这些规则的方法就会被拦截。值得一提的是,这儿匹配的是方法的完整路径,包括类名已经类的包名。回顾一下,这儿的目标类是com.service.UserService,它里面只有两个方法addUser和deleteUser。这两个方法的完整路径就是com.service.UserService.addUser 和 com.service.UserService.deleteUser 。这儿写的正则表达式是.*deleteUser ,明显它可以匹配com.service.UserService.deleteUser,所以这个方法会被拦截。假设将正则表达式写成deleteUser,那么它不能匹配com.service.UserService.deleteUser 这整个字符串,所以讲不会起作用。如果要拦截方法名中包含abc的方面,正则表达式写成.*abc.*就可以了(当然包名和类名不能是abc啦)。
最后看看环绕通知:
<!-- 定义环绕通知 --> <bean id="logAroundAdvice" class="com.aop.LogAroundAdvice"></bean> <bean id="myAroundAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedNames"> <list> <value>*User</value> </list> </property> <property name="advice"> <ref local="logAroundAdvice" /> </property> </bean>
这里用到了NameMatchMethodPointcutAdvisor,它的mappedNames属性下添加想要拦截的方法名就可以了,当然也可以用*通配符啦,*User就代表所有以User结尾的方法。注意这儿只匹配方法名,不会管类名和包名了。
工程代码在附件中。。。。