Spring的AOP详解
要实现spring的aop编程,必须要知道几个概念:
1. 通知:就是交叉业务的代码,作用于目标对象中。好处是把枝节性代码和主干性代码分离开。
通知可分4种类型:
a. 前置通知:在目标方法执行之前调用。
实现MethodBeforeAdvice接口,接口方法如下:
void before(Method method,Object[] args,Object target) throws Throwable;
这个接口为我们提供了获得目标方法,参数,目标对象的机会。注意:我们没必要在这个方法里面调用目标方法(可以通过反射调用),因为,这个接口方法结束后目标方法将调用,这才叫前置嘛(在目标方法之前)。要想终止程序的继续运行,只有抛异常或调用System.exit()
b. 后置通知:在目标方法执行之后调用。
实现AfterReturningAdvice接口,该接口方法如下:
void afterReturning(Object returnValue,Method method,
Object[] args, Object target) throws Throwable
这个方法多了一个目标方法的返回值参数。同理要结束程序流程只有抛异常和调用System.exit()方法。
c. 环绕通知:拦截目标方法的执行,可在该类型通知里自行调用目标方法。
实现MethodInterceptor接口,该接口在aopalliance.jar包里,这个包是aop联盟(aop编程接口,为实现aop编程接口重用)的包,可以理解成aop编程规范接口。
接口方法如下:
Object invoke(MethodInvocation invocation) throws Throwable;
环绕通知和上述2个通知的区别:
1. 由于环绕通知是拦截目标方法的执行,所以在这个方法里用户可以通过invocation.proceed()方法调用目标方法,这点和MethodBeforeAdvice不同,它的目标方法总是会执行,除非抛异常或执行System.exit()方法。
2. 正常情况下这个方法返回的就是目标方法(invocation.proceed()返回的结果)的返回值,但可以在这个接口方法里改变目标方法的返回值,除非必须如此,否则最好不要这样使用。这个接口与AOP联盟的AOP框架兼容。
因为要显示的调用目标方法(invocation.proceed()),所以更推荐使用上述2种方式。
d. 异常通知:当目标方法抛出异常时调用。
实现ThrowsAdvice接口,该接口是标记接口,没有定义任何一个必须实现的方法,但实现该接口的类必须至少有一个如下形式的方法:
public void afterThrowing(Exception ex)
public void afterThrowing(RemoteException)
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
2. 切入点:就是插入通知的地方,由于spring是建立在方法拦截之上的,也就是说spring的切入点就是一个方法(当然是满足一定条件的方法)。
Spring有2中方式创建切入点
a. 自定义切入点:必须实现MethodMatcher,他有3个方法:
boolean | |
boolean | |
boolean |
第一步,根据类和方法决定一个方法是否该被通知,如果满足条件返回true则isRuntime()方法被调用。isRuntime()方法被调用来决定是静态切入点还是动态切入点。
a. 静态切入点:通知总是被执行。isRuntime()方法返回false,matches方法将不再执行。静态切入点只在代理创建的时候执行一次,而不是运行期间,每次方法调用都执行。
运用spring自带的静态切入点:
1. NameMatchMethodPointcut,他有2个方法
public void setMappedName(String name);
public void setMappedNames(String[] names):
当被调用方法的名字和给出的映射名字匹配时,切点才匹配。方法1是匹配一个字符串,方法2是匹配一组字符串。
下面展示如何运用该切入点实现映射:
<bean id="theAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName">
<value>set*</value> //匹配所有set方法
</property>
//或者用一组字符串去匹配,nappedName和nappedNames不能同时存在
<property name="mappedName">
<list>
<value>set*</value>
<value>get*</value>
</list>
</property>
<property name="advice">
<ref bean="freeAdvice"/>
</property>
</bean>
上面这种方式是直接组装成一个Advisor了,也可拆开配置,如下:
<bean id="pointcutBean"
class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>set*</value>
</list>
</property>
</bean>
<bean id="defaultAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref=" beforeAdvice "/>
<property name="pointcut" ref="pointcutBean" />
</bean>
DefaultPointcutAdvisor类的使用很简单,他有一个 advice及 pointcut 属性,advice属性用来指明要使用的通知,pointcut属性用来指定切入点,我们可以通过构造子或设值注入方式来配置这个 Bean,上面选用的是set注入配置。
2. RegexpMethodPointcut:利用正则表达式来定义切入点。RegexpMethodPointcut的详细使用方法和NameMatchMethodPointcut无多大区别,详细资料自己网上查阅。
b. 动态切入点:isRuntime()方法返回true,则执行matches方法,该方法通过参数动态判断通知是否要执行。因为每次调用目标方法时都要执行判断,所以动态切入点效率上明显更慢。
动态切入点还在研究中……