欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring AOP面向切面编程

程序员文章站 2022-06-04 22:03:41
...

      最近在系统中需要实现用户某些操作添加积分, 希望实现对系统现有的代码进行最小嵌入,因此使用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)

             前三种都是基于代理的AOP的变体,因此,Spring对AOP的支持局限于方法注入。如果我们的AOP需求超过了简单方法注入的范畴(比如构造器或属性注入),就应该考虑在AspectJ里实现切面,利用Spring的从属注入把Spring的Bean注入到AspectJ切面

 

  • 在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>  
  • 自动代理

 

              自动代理能够让切面的切点定义来决定哪个Bean需要代理,不需要我们为特定的Bean明确地创建代理,从而提供了一个更完整的AOP实现。实现自动代理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

 

           切面的功能被称为Advice(通知),定义切面的工作内容,以及切面工作的时机,实在被拦截方法之前,之后,异常抛出时,还是以上情况都执行。

  • 连接点Joinpoint

             joinpoint就是程序执行过程中能够插入到切面的一个点,这个点就是方法被调用时,异常抛出时,甚至字段被编辑时。切面代码通过这个点插入到程序的一般流程中,从而添加新的行为。

  • 切入点Pointcut

              pointcut作用就是缩小切面通知范围

             切入点可以缩小切面通知的连接点的范围。如果说advice定义了切面的“什么”和“何时”,那么切入点就定义了“何地”。切入点的定义匹配advice要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义匹配的类和方法名称模板来指定这些切入点。有些AOP框架允许创建动态切入点,可以根据运行时的状态(比如方法的参数值)来应用通知。

  •  切面Aspect
              切面就是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能。

  • 目标Target
              “目标”是被通知的对象,它可以是我们编写的一个对象,或第三方对象。如果没有AOP,这个对象就必须包含自己的主要逻辑和交叉事务的逻辑。通过使用AOP,目标对象就可以着重于自己的主要逻辑,不必考虑要被应用的任何通知。

  • 代理Proxy
              代理是向目标对象应用通知之后被创建的对象。对于客户对象来说,目标对象(AOP之前)和代理对象(AOP之后)是一样的——它们就应用是这样的。这样一来,程序的其他部分就不必修改对代理对象的支持。

  • 织入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