最近在系统中需要实现用户某些操作添加积分, 希望实现对系统现有的代码进行最小嵌入,因此使用Spring AOP的切面编程将很好实现该需求,达到最小耦合度。在Spring AOP中的通知都是针对方法层级进行通知,相对与Struct中针对类层级通知,具有更好的灵活性。
/*方法拦截*/
MethodInterceptor
/*返回拦截*/
AfterReturningAdvice
/*事件拦截*/
HandlerInterceptor
MethodInvocation
- Spring对AOP的支持具有以下4种情况:
1、经典的基于代理的AOP(各版本Spring)
2、@AspectJ注解驱动的切面(仅Spring 2.0);
3、纯POJO切面(仅Spring2.0);
4、注入式AspectJ切面(各版本Spring)
- 在spring aop中有以下四种通知类型,本次使用的是after returning advice方式的通知。
1、before advice 在方法执行前执行。
2、after returning advice 在方法执行后返回一个结果后执行
3、after throwing advice 在方法执行过程中抛出异常后执行
4、around advuce 综合执行以上三种情况
- 典型Spring AOP编程的三个步骤
1、创建通知:实现通知的这几个接口,并实现其中的方法
a)org.springframework.aop.MethodBeforeAdvice
b)org.springframework.aop.AfterReturningAdvice
c)org.springframework.aop.ThrowsAdvice
d)org.aopalliance.intercept.MethodInterceptor
e)org.springframework.aop.IntroductionInterceptor
2、定义切点和通知者:在Spring配制文件中配置这些信息
3、使用ProxyFactoryBean来生成代理
- 创建通知advice
public class GuideIntegralAfterAdvice implements AfterReturningAdvice{ /*监听器会监听所有的方法,并且不管监听方法是否执行成功,都会执行监听方法*/ @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { //监听方法的返回值 System.out.println("返回值"+arg0); //实现的监听方法名称 System.out.println("Method:"+arg1.getName()); //监听方法所携带的参数 System.out.println("arg2[0]:"+arg2[0]); //方法调用的目标(也就是被调用方法所在的对象) System.out.println("arg3:"+arg3); } }
- 定义切入点pointcut和通知
要想实现Spring AOP 仅仅针对某一指定的方法进行切面编程,而不是针对所有的方法进行切面编程,那么需要指定切入点(pointcut) ,pointcut通过制定方法名进行拦截方法,同时pointcut必须advisor进行关联。pointcut可以通过指定完全方法名或通过正则表达式进行匹配方法。
- 定义一个通过完全方法名匹配方法的切入点
<bean id="namePointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="insertTestStudent" />
</bean>
org.springframework.aop.support.NameMatchMethodPointcut:表示该切点通过直接通过完全方法名匹配方法的,property中的value就是完整的方法名。
- 通过正则表达式匹配方法
<bean id="addGuideIntegralAfterTesting" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value=".*insertTestStudent"/>
</bean>
结合通知者advisor
<bean id="audienceAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="pointcut" ref="performancePointcut" />
</bean>
org.springframework.aop.support.DefaultPointcutAdvisor是个通知者类,他只是把通知关联给切入点
- 联合切入点
联合切入点是比较特殊的正则表达式切入点,他同时结合了切入点和通知者
<bean id="addGuideIntegralAfterTesting" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="guideIntegralAfterAdvice" />
<property name="patterns">
<list>
<value>.*insertTestStudent</value>
</list>
</property>
</bean>
- AspectJ 切点
从AspectJ里定义切点的方式就可以看出AspectJ的切点语言是一种真正的切点表达语言。 类org.springframework.aop.aspectj.AspectJExpressionPointcut被用来定义AspectJ切点表达式
<bean id="performancePointcut" class="org.springframework.aop.aspectj.AspectJExpressionPointcut">
<property name="expression" value="execution(* Performer+.perform(..))" />
</bean>
通过使用DefaultPointcutAdvisor把切入点和通知者结合起来。我们可以利用特殊的通知者,把切点表达式定义为通知者的一个属性。对于AspectJ表达式来说,使用的通知者类是org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor:
<bean id="audienceAdvisor" class="org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="advice" ref="audienceAdvice" />
<property name="expression" value="execution(* Performer+.perform(..))" />
</bean>
通知者把通知与切点关联起来,从而完整地定义一个切面。
- Spring 代理
切面在Spring里是以代理方式实现的,所以仍然需要代理目标Bean才能让通知者发挥作用,以下有两种方式显示代理,一通过ProxyFactoryBean方式或者通过自动代理。
<!--定义个联合代理-->
<bean id="addIntegralAutoProxyAop" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<!--需要监听的接口-->
<value>guideStudentService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<!-- 与第一个bean的id相匹配 -->
<value>addGuideIntegralAfterTesting</value>
</list>
</property>
</bean>
- ProxyFactoryBean代理
Spring的ProxyFactoryBean是个工厂Bean,用于生成一个代理,把一个或多个通知者应用到Bean
<bean id="duke" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="dukeTarget" />
<property name="interceptorNames">
<list>
<value>audienceAdvisor</value>
</list>
</property>
</bean>
- 自动代理
1、基于Spring上下文里声明的通知者Bean的基本自动代理”:通知者的切点表达式用于决定哪个Bean和哪个方法要被代理。
2、基于@AspectJ注解驱动切面的自动代理”:切面里包含的通知里指定的切点将用于选择哪个Bean和哪个方法要被代理。
<!--定义个联合代理-->
<bean id="addIntegralAutoProxyAop" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<!--需要监听的接口-->
<value>guideStudentService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<!-- 与第一个bean的id相匹配 -->
<value>addGuideIntegralAfterTesting</value>
</list>
</property>
</bean>
- 完整的配置信息
<!-- 定义通知advice -->
<bean id="guideIntegralAfterAdvice" class="com.rrtong.interceptors.GuideIntegralAfterAdvice" />
<!-- 定义pointcut和通知者advisor的联合切入点和通知者 -->
<bean id="addGuideIntegralAfterTesting" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 引用通知接口的实现类 -->
<property name="advice" ref="guideIntegralAfterAdvice" />
<!-- 正则表达式匹切入点方法名 -->
<property name="patterns">
<list>
<value>.*insertTestStudent</value>
</list>
</property>
</bean>
<!-- 定义自动代理 -->
<bean id="addIntegralAutoProxyAop" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>guideStudentService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>addGuideIntegralAfterTesting</value>
</list>
</property>
</bean>
- 通知Advice
- 连接点Joinpoint
joinpoint就是程序执行过程中能够插入到切面的一个点,这个点就是方法被调用时,异常抛出时,甚至字段被编辑时。切面代码通过这个点插入到程序的一般流程中,从而添加新的行为。
- 切入点Pointcut
pointcut作用就是缩小切面通知范围
- 切面Aspect
- 目标Target
- 代理Proxy
- 织入Weaving
- 参考资料
http://blog.csdn.net/sin90lzc/article/details/7486145
http://blog.163.com/zzf_fly/blog/static/209589158201382314454298/
http://blog.csdn.net/topwqp/article/details/8695180