1. 程式人生 > >Spring AOP 後篇(三): AOP切面程式設計

Spring AOP 後篇(三): AOP切面程式設計

Spring AOP 後篇: AOP切面程式設計

該文章參考多篇文章的基礎上進行了簡化並做少許修改,方便理解。原文章地址如下:

  1. Spring3:AOP
  2. Spring的AOP面向切面程式設計

一、理解切入點表示式(execution())(重點)

  • 切入點指示符

    • execution:用於匹配方法執行連線點。這是使用Spring AOP時使用的主要切入點指示符。
    • within:限制匹配某些型別中的連線點(使用Spring AOP時在匹配型別中宣告的方法的執行)。
    • this:限制與連線點的匹配(使用Spring AOP時執行方法),其中bean引用(Spring AOP代理)是給定型別的例項。
    • target:限制匹配連線點(使用Spring AOP時執行方法),其中目標物件(被代理的應用程式物件)是給定型別的例項。
    • args:限制與連線點的匹配(使用Spring AOP時執行方法),其中引數是給定型別的例項。
    • @target:限制與連線點的匹配(使用Spring AOP時執行方法),其中執行物件的類具有給定型別的註釋。
    • @args:限制與連線點的匹配(使用Spring AOP時執行方法),其中傳遞的實際引數的執行時型別具有給定型別的註釋。
    • @within:限制匹配到具有給定註釋的型別中的連線點(使用Spring AOP時執行在具有給定註釋的型別中宣告的方法)。
    • @annotation:限制連線點的匹配,其中連線點的主題(在Spring AOP中執行的方法)具有給定的註釋。

    within、this、args等詳細解釋。。。(todo)

  • execution表示式 詳解參考Spring AOP 官方文件

    <!-- * 號表示萬用字元; 
    如下 execution表示式表示 匹配service包下的所有類的所有帶參及不帶引數的方法 -->
    <aop:pointcut id="businessServcie" 
        expression="execution(* com.xyz.myapp.service.*.*(..))"
    />
    1. execution() : 表示式主體
    2. 第一個’*'號位置:表示 返回型別;*作用是匹配任何返回型別;
    3. com.xyz.myapp.service: 表示包名,即service包
    4. 第二個’*'號位置:表示 類名;*作用是匹配該包下的所有類
    5. 第三個‘*’號位置:表示 方法名; *作用是匹配所有方法
    6. (…): 表示 括號裡面代表引數;()表示匹配一個不帶引數的方法;(…)表示匹配任何數量(零個或多個)引數;(*)表示匹配一個採用任何型別引數的方法

切入點其他表示式

  • 常用切入點表示式(execution())Spring AOP官方文件

    1. 執行任何公共方法:
    execution(public * *(..))
    
    1. 執行名稱以以下開頭的任何方法set
    execution(* set*(..))
    
    1. 執行AccountService介面定義的任何方法:
    execution(* com.xyz.service.AccountService.*(..))
    
    1. 執行service包中定義的任何方法:
    execution(* com.xyz.service.*.*(..))
    
    1. 執行服務包或其子包中定義的任何方法:
    execution(* com.xyz.service..*.*(..))
    
    1. 服務包中的任何連線點(僅在Spring AOP中執行方法):
    within(com.xyz.service.*)
    
    1. 服務包或其子包中的任何連線點(僅在Spring AOP中執行方法):
    within(com.xyz.service..*)
    
    1. 代理實現AccountService介面的任何連線點(僅在Spring AOP中執行方法) :
    this(com.xyz.service.AccountService)
    

二、XML手動配置方式

  1. aopBean.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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
        
    	<!-- dao 例項 -->
        <bean id="userDao" class="com.aop.UserDao" />
        <bean id="orderDao" class="com.aop.OrderDao" />
    	<!-- 切面類:在呼叫dao類時,先後呼叫 時間列印及日誌列印類 -->
        <bean id="timePrint" class="com.aop.TimePrint" />
        <bean id="logPrint" class="com.aop.LogPrint" />
        
        <aop:config>
            <!-- 定義切面類
			ref: 指定切面類; 
			order(int):指定切面的優先順序來控制通知的執行順序; order1 》order2
 			-->
            <aop:aspect id="time" ref="timePrint" order="1">
                <!-- 切入點
 				expression: 定義切入點表示式;即定義要切入的類或方法
				-->
                <aop:pointcut id="addTime" expression="execution(* com.aop.userDao.*(..))" />
                <!-- 前置通知: 在目標方法呼叫前執行 
				method:切面類執行的方法;即目標類呼叫前執行的方法
				-->
                <aop:before method="printTime" pointcut-ref="addTime" />
                <!-- 後置通知: 在目標方法呼叫後執行 -->
                <aop:after method="printTime" pointcut-ref="addTime" />
            </aop:aspect>
            
            <aop:aspect id="log" ref="logPrint" order="2">
                <aop:pointcut id="printLog" expression="execution(* com.aop.*.*(..))" />
                <aop:before method="LogBefore" pointcut-ref="printLog" />
                <aop:after method="LogAfter" pointcut-ref="printLog" />
            </aop:aspect>
        </aop:config>
</beans>
  1. aopBean.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"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- dao 例項   -->
        <bean id="userDao" class="com.aop.dao.UserDao"></bean>
        <bean id="orderDao" class="com.aop.dao.OrderDao"></bean>
        
        <!-- 切面類 -->
        <bean id="aop" class="com.aop.dao.Aop"></bean>
        
        <!-- Aop配置 -->
        <aop:config>
            <!-- 定義一個切入點表示式: 攔截哪些方法 -->
            <aop:pointcut  id="pt" expression="execution(* com.aop.dao.*.*(..))"/>
            <!-- 切面 -->
            <aop:aspect ref="aop">
                <!-- 環繞通知 -->
                <aop:around method="around" pointcut-ref="pt"/>
                <!-- 前置通知: 在目標方法呼叫前執行 -->
                <aop:before method="begin" pointcut-ref="pt"/>
                <!-- 後置通知: -->
                <aop:after method="after" pointcut-ref="pt"/>
                <!-- 返回後通知 -->
                <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
                <!-- 異常通知 -->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
                
            </aop:aspect>
        </aop:config>
    </beans>
    

三、@Aspectj 註解方式

  1. bean.xml 配置aop註解掃描

    <?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"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <!-- 使用註解時要開啟註解掃描 要掃描的包 -->
        <context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan>
        
        <!-- 開啟aop註解方式 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    
    1. 使用切面類使用@Aspect 註解

      @Component  //加入IOC容器
      @Aspect  // 指定當前類為切面類
      public class Aop {
      
          // 指定切入點表示式: 攔截哪些方法; 即為哪些類生成代理物件
        //解釋@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
        //@Pointcut("execution(*    切入點表示式固定寫法, cn.itcast.e_aop_anno表示包.類名(可以用*表示包下所有的類).方法名(可以用*表示類下所有的方法)(..)表示引數可以用..
          @Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
          public void pointCut_(){
          }
          
        //@Before("execution(* cn.itcast.e_aop_anno.*.*(..))")每個方法需要寫相同的引用,所以將相同的部分抽取到一個空的方法中pointCut_(),
          // 前置通知 : 在執行目標方法之前執行
          @Before("pointCut_()")
          public void begin(){
              System.out.println("開始事務/異常");
          }
          
          // 後置/最終通知:在執行目標方法之後執行  【無論是否出現異常最終都會執行】
          @After("pointCut_()")
          public void after(){
              System.out.println("提交事務/關閉");
          }
          
          // 返回後通知: 在呼叫目標方法結束後執行 【出現異常不執行】
          @AfterReturning("pointCut_()")
          public void afterReturning() {
              System.out.println("afterReturning()");
          }
          
          // 異常通知: 當目標方法執行異常時候執行此關注點程式碼
          @AfterThrowing("pointCut_()")
          public void afterThrowing(){
              System.out.println("afterThrowing()");
          }
          
          // 環繞通知:環繞目標方式執行
          @Around("pointCut_()")
          public void around(ProceedingJoinPoint pjp) throws Throwable{
              System.out.println("環繞前....");
              pjp.proceed();  // 執行目標方法
              System.out.println("環繞後....");
          }
          
      }