1. 程式人生 > >淺析spring AOP面向切面程式設計

淺析spring AOP面向切面程式設計

                                                                 關於spring AOP 程式設計的理解

spring 的AOP程式設計有兩種實現方式,第一種是sping的動態代理,該AOP是一種實現其介面的方式,進行切面程式設計,另外一種方式是cglib代理,該代理方式原理是繼承自己的類。

spring的配置檔案如下:

 <aop:config proxy-target-class="false">  <!-- proxy-target-class="false" 表示動態代理(系統預設),proxy-target-class="true" cglib代理       -->
 </aop:config> 

AOP配置方式,一種是xml配置檔案方式,另一種是註解方式。下面詳細介紹兩種方式的用法:

 <aop:config proxy-target-class="false">
        <aop:aspect id="log" ref="userManualProxy" >
               <aop:pointcut id="login"
                      expression="execution(* com.sheemes.service.impl.LoginServiceImpl.*(..))" />
                <aop:after method="loginInfo" pointcut-ref="login" />
        </aop:aspect>
    </aop:config> 

AOP中的相關概念

Aspect(切面): Aspect 宣告類似於 Java 中的類宣告,在 Aspect 中會包含著一些 Pointcut 以及相應的 Advice。
Joint point(連線點):表示在程式中明確定義的點,典型的包括方法呼叫,對類成員的訪問以及異常處理程式塊的執行等等,它自身還可以巢狀其它 joint point。
Pointcut(切點):表示一組 joint point,這些 joint point 或是通過邏輯關係組合起來,或是通過通配、正則表示式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
Advice(增強):Advice 定義了在 Pointcut 裡面定義的程式點具體要做的操作,它通過 before、after 和 around 來區別是在每個 joint point 之前、之後還是代替執行的程式碼。
Target(目標物件):織入 Advice 的目標物件.。
Weaving(織入):將 Aspect 和其他物件連線起來, 並建立 Adviced object 的過程

然後舉一個容易理解的例子: 
看完了上面的理論部分知識, 我相信還是會有不少朋友感覺到 AOP 的概念還是很模糊, 對 AOP 中的各種概念理解的還不是很透徹. 其實這很正常, 因為 AOP 中的概念是在是太多了, 我當時也是花了老大勁才梳理清楚的. 
下面我以一個簡單的例子來比喻一下 AOP 中 Aspect, Joint point, Pointcut 與 Advice之間的關係. 
讓我們來假設一下, 從前有一個叫爪哇的小縣城, 在一個月黑風高的晚上, 這個縣城中發生了命案. 作案的凶手十分狡猾, 現場沒有留下什麼有價值的線索. 不過萬幸的是, 剛從隔壁回來的老王恰好在這時候無意中發現了凶手行凶的過程, 但是由於天色已晚, 加上凶手蒙著面, 老王並沒有看清凶手的面目, 只知道凶手是個男性, 身高約七尺五寸. 爪哇縣的縣令根據老王的描述, 對守門的士兵下命令說: 凡是發現有身高七尺五寸的男性, 都要抓過來審問. 士兵當然不敢違背縣令的命令, 只好把進出城的所有符合條件的人都抓了起來.

來讓我們看一下上面的一個小故事和 AOP 到底有什麼對應關係. 
首先我們知道, 在 Spring AOP 中 Joint point 指代的是所有方法的執行點, 而 point cut 是一個描述資訊, 它修飾的是 Joint point, 通過 point cut, 我們就可以確定哪些 Joint point 可以被織入 Advice. 對應到我們在上面舉的例子, 我們可以做一個簡單的類比, Joint point 就相當於 爪哇的小縣城裡的百姓,pointcut 就相當於 老王所做的指控, 即凶手是個男性, 身高約七尺五寸, 而 Advice 則是施加在符合老王所描述的嫌疑人的動作: 抓過來審問. 
為什麼可以這樣類比呢?

Joint point : 爪哇的小縣城裡的百姓: 因為根據定義, Joint point 是所有可能被織入 Advice 的候選的點, 在 Spring AOP中, 則可以認為所有方法執行點都是 Joint point. 而在我們上面的例子中, 命案發生在小縣城中, 按理說在此縣城中的所有人都有可能是嫌疑人.

Pointcut :男性, 身高約七尺五寸: 我們知道, 所有的方法(joint point) 都可以織入 Advice, 但是我們並不希望在所有方法上都織入 Advice, 而 Pointcut 的作用就是提供一組規則來匹配joinpoint, 給滿足規則的 joinpoint 新增 Advice. 同理, 對於縣令來說, 他再昏庸, 也知道不能把縣城中的所有百姓都抓起來審問, 而是根據凶手是個男性, 身高約七尺五寸, 把符合條件的人抓起來. 在這裡 凶手是個男性, 身高約七尺五寸 就是一個修飾謂語, 它限定了凶手的範圍, 滿足此修飾規則的百姓都是嫌疑人, 都需要抓起來審問.

Advice :抓過來審問, Advice 是一個動作, 即一段 Java 程式碼, 這段 Java 程式碼是作用於 point cut 所限定的那些 Joint point 上的. 同理, 對比到我們的例子中, 抓過來審問 這個動作就是對作用於那些滿足 男性, 身高約七尺五寸 的爪哇的小縣城裡的百姓.

Aspect::Aspect 是 point cut 與 Advice 的組合, 因此在這裡我們就可以類比: “根據老王的線索, 凡是發現有身高七尺五寸的男性, 都要抓過來審問” 這一整個動作可以被認為是一個 Aspect.

最後是一個描述這些概念之間關係的圖: 

这里写图片描述

Advice 的型別

before advice, 在 join point 前被執行的 advice. 雖然 before advice 是在 join point 前被執行, 但是它並不能夠阻止 join point 的執行, 除非發生了異常(即我們在 before advice 程式碼中, 不能人為地決定是否繼續執行 join point 中的程式碼)

after return advice, 在一個 join point 正常返回後執行的 advice

after throwing advice, 當一個 join point 丟擲異常後執行的 advice
after(final) advice, 無論一個 join point 是正常退出還是發生了異常, 都會被執行的 advice.
around advice, 在 join point 前和 joint point 退出後都執行的 advice. 這個是最常用的 advice.
introduction,introduction可以為原有的物件增加新的屬性和方法。

程式碼如下:(配置檔案)

 

<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

   <aop:config>
      <aop:pointcut id="selectAll" 
                    expression="execution(* com.aop.*.getName(..))"/>
    <aop:pointcut expression="execution(* com.aop.*.getAge(..))" id="getAge"/>            
      <aop:aspect id="log" ref="log1">
          <aop:before pointcut-ref="selectAll"  pointcut="name" method="beforeAdvice"/>
            <aop:after pointcut-ref="selectAll" method="afterAdvice"/>
            <aop:before method="beforeAdvice" pointcut-ref="getAge"/>
            <aop:after method="afterAdvice" pointcut-ref="getAge"/> 
      </aop:aspect>
   </aop:config>

   <!-- Definition for student bean -->
   <bean id="student" class="com.aop.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11"/>      
   </bean>

   <!-- Definition for logging aspect -->
   <bean id="log1" class="com.aop.Logging"/> 

</beans>

 <!-- Definition for student bean -->
   <bean id="student" class="com.aop.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11"/>      
   </bean>

   <!-- Definition for logging aspect -->
   <bean id="log1" class="com.aop.Logging"/> 

package com.aop;
public class Logging {
    /** 
        * This is the method which I would like to execute
        * before a selected method execution.
        */
    

       public void beforeAdvice(String name){
          System.out.println("Going to setup student profile."+name);
       }
       /** 
        * This is the method which I would like to execute
        * after a selected method execution.
        */
       public void afterAdvice(String name){
           if("小明".equals(name)) {
               System.out.println("小明登入了系統");
           }
          System.out.println("Student profile has been setup."+name);
       }
       /** 
        * This is the method which I would like to execute
        * when any method returns.
        */
       
       public void afterReturningAdvice(Object retVal){
          System.out.println("Returning:" + retVal.toString() );
       }
       /**
        * This is the method which I would like to execute
        * if there is an exception raised.
        */
       public void AfterThrowingAdvice(IllegalArgumentException ex){
          System.out.println("There has been an exception: " + ex.toString());   
       }  
}

------------------------------註解形式的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:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

   <aop:aspectj-autoproxy/>

   <!-- Definition for student bean -->
   <bean id="student" class="com.aop.Student">
      <property name="name"  value="Zara" />
      <property name="age"  value="11"/>      
   </bean>

   <!-- Definition for logging aspect -->
   <bean id="log1" class="com.aop.Logging"/> 

</beans>

package com.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class Logging {
       @Pointcut("execution(* com.aop.Student.set*(..))")
       public void setAll() {};
       @Pointcut("execution(* com.aop.Student.get*(..))")
       public void getAll() {};
       
    /** 
        * This is the method which I would like to execute
        * before a selected method execution.
        */
    
       @Before("setAll()&& args(name)")
       public void beforeAdvice(String name){
          System.out.println("Going to setup student profile."+name);
       }
       /** 
        * This is the method which I would like to execute
        * after a selected method execution.
        */
       @AfterReturning(pointcut="getAll()",returning="name")
       public void afterAdvice(String name){
           if("小明".equals(name)) {
               System.out.println("小明登入了系統");
           }
          System.out.println("Student profile has been setup."+name);
       }
       /** 
        * This is the method which I would like to execute
        * when any method returns.
        */
       
       public void afterReturningAdvice(Object retVal){
          System.out.println("Returning:" + retVal.toString() );
       }
       /**
        * This is the method which I would like to execute
        * if there is an exception raised.
        */
       public void AfterThrowingAdvice(IllegalArgumentException ex){
          System.out.println("There has been an exception: " + ex.toString());   
       }  
}

package com.aop;

public class Student {
       private Integer age;
       private String name;
       public void setAge(Integer age) {
          this.age = age;
       }
       public Integer getAge() {
          System.out.println("Age : " + age );
          return age;
       }
       public void setName(String name) {
          this.name = name;
       }
       public String getName() {
          System.out.println("Name : " + name );
          return null;
       }  
       public void printThrowException(){
           System.out.println("Exception raised");
          // throw new IllegalArgumentException();
       }
}

package com.show;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.aop.Student;

public class MainApp2 {
    public static void main(String[] args) {
      ApplicationContext context = new ClassPathXmlApplicationContext("spring2.xml");
      Student student=(Student)context.getBean("student");
      student.setName("小明");
      student.getName();
     // student.getAge();      
    //  student.printThrowException();
    }
}
面向切面AOP的引數傳遞過程

表示式中的引數必須和切點方法中的引數名稱一致,1.表示式引數名稱匹配切點方法引數名稱,根據切點方法名稱找到切點方法的型別,2.連線點方法引數名稱,型別匹配切點方法引數名稱和型別。

表示式中*表示任意字串,..表示任意引數