Hello Spring-AOP

james發表於2017-11-19

OOP(Object Oriented Programming)物件導向程式設計解決了縱向上的層次分割,例如MVC模式將展示層、持久化層、邏輯處理層一一分開了,使得開發效率得到了較大提高,但是這只是縱向上的分割,如果從水平方向上來看,即如果一個專案中的很多類都需要實現某一個方法(例如:在網購平臺上,登入,下單,付款都需要對身份進行驗證)如果要在這些類一一將這些都要執行的方法新增到其中這無疑將會增加程式碼量,這會使得程式顯得很臃腫。基於以上的操作便誕生了我們今天的主角—-Spring-AOP。AOP即(Aspect Oriented Programming)面向切面程式設計。Spring-AOP就是實現一閃剛提出的問題。接下來我們就為大家揭開Spring-AOP的“神祕面紗”。

1 AOP簡介

1.1 什麼是AOP

AOP(Aspect Oriented Programming)面向切面程式設計,是對傳統的OOP(ObjectOriented Programming)物件導向程式設計的補充。

1.2 AOP的作用

如果A,B,C三個方法都要在執行前做驗證操作,執行後做日誌列印操作。腫麼辦?
![圖片描述][1]

1.3 AOP專業術語

   切面(Aspect): A,B,C,方法執行前都要呼叫的驗證邏輯和執行後都要呼叫的日誌邏輯,這兩個就是切面。

通知(Advice): 有五種通知,執行前,執行後,執行成功後,執行丟擲異常後,環繞通知。就是切面執行的方法。
目標(Target): 被通知的物件,這裡就是A,B,C三個方法。
連線點(Joinpoint):連線點是一個應用執行過程中能夠插入一個切面的點。
切點(pointcut):每個類都擁有多個連線點,即連線點是程式類中客觀存在的事務。AOP 通過切點定位到特定的連線點
打個比方:一天,三位俠客(被通知的物件Target)來我府上做客,被大門(切面Aspect)攔住,門前有五個保安(負責通知的Advice),因為其中一位俠客會降龍十八掌(滿足被通知的一個條件Joinpoint),其中一位保安告知他:”你可以進去了”。另外兩個俠客因為武藝超群(滿足被通知的統一標準poincut)也都進去了。

例項講解:

下面我們以一段例項來講解基於XML配置檔案和基於註解的方式來說明Spring-AOP的執行機制。
該程式的整體執行機制是:將入一個專案都想執行加、減、乘、除這四個方法,所以我們將這四個操作封裝在一個類中並將其作為一個切面,從而提高程式碼的複用率。

  1. 基於XML的使用

    //這個便是我們的切面,即許多類要執行的方法
    package com.zhangguo.Spring052.aop01;
    public class Math {

         //加
       public int add(int n1,int n2){
           int result=n1+n2;
           System.out.println(n1+"+"+n2+"="+result);
           return result;
       }
       
       //減
       public int sub(int n1,int n2){
           int result=n1-n2;
           System.out.println(n1+"-"+n2+"="+result);
           return result;
       }
       
       //乘
       public int mut(int n1,int n2){
           int result=n1*n2;
           System.out.println(n1+"X"+n2+"="+result);
           return result;
       }
       
       //除
       public int div(int n1,int n2){
           int result=n1/n2;
           System.out.println(n1+"/"+n2+"="+result);
           return result;
       }

    }

//這個就是通知啦,好比是上面所打比方的那五個門衛,所有類要使用上面的切面首先要經過這個類的“稽核”

package com.zhangguo.Spring052.aop01;
import org.aspectj.lang.JoinPoint; 
public class Advices {

     public void before(JoinPoint jp){
            System.out.println("----------前置通知----------");
            System.out.println(jp.getSignature().getName());
        }    
     public void after(JoinPoint jp){
            System.out.println("----------最終通知----------");
        }
}

//下面我們列出本例項的核心,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: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/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
       
    <!-- 被代理物件 -->
    <!-- 此為在橫向切面被統一抽象出來的方法 -->
    <bean id="math" class="com.zhangguo.Spring052.aop01.Math"></bean>
    
    <!-- 通知 -->
    <bean id="advices" class="com.zhangguo.Spring052.aop01.Advices"></bean>
    
    <!-- aop配置 -->
    <aop:config proxy-target-class="true">
        <!--切面 -->
        <aop:aspect ref="advices">
                  <!-- 切點 -->

<aop:pointcut expression=”execution( com.zhangguo.Spring052.aop01.Math.(..))”id=”pointcut1″/>

                  <!--連線通知方法與切點 -->
                  //當實現了通知中的before方法時執行切面中方法
                  <aop:before method="before" pointcut-ref="pointcut1"/>
                  //當實現了通知中的after方法時執行切面中方法
                  <aop:after method="after" pointcut-ref="pointcut1"/>
        </aop:aspect>
    </aop:config>

</beans>



  
 //此為測試類
package com.zhangguo.Spring052.aop01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
     
     public static void main(String[] args) {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("aop01.xml");
            Math math = ctx.getBean("math", Math.class);
            int n1 = 100, n2 = 5;
            math.add(n1, n2);
            math.sub(n1, n2);
            math.mut(n1, n2);
            math.div(n1, n2);
    }
}





  1. 基於註解的使用

package com.zhangguo.Spring052.aop02;
import org.springframework.stereotype.Service; 

//此處使用@Service說明該類為切面
@Service("math")
public class Math{
    //加
    public int add(int n1,int n2){
        int result=n1+n2;
        System.out.println(n1+"+"+n2+"="+result);
        return result;
    }
    
    //減
    public int sub(int n1,int n2){
        int result=n1-n2;
        System.out.println(n1+"-"+n2+"="+result);
        return result;
    }
    
    //乘
    public int mut(int n1,int n2){
        int result=n1*n2;
        System.out.println(n1+"X"+n2+"="+result);
        return result;
    }
    
    //除
    public int div(int n1,int n2){
        int result=n1/n2;
        System.out.println(n1+"/"+n2+"="+result);
        return result;
    }
}



package com.zhangguo.Spring052.aop02;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Advices {
//此註解可以將Advices設定為通知類,即當使用到before方法時,便執行execution方括號中的號碼
    @Before("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
    public void before(JoinPoint jp){
        System.out.println("----------前置通知----------");
        System.out.println(jp.getSignature().getName());
    }
    
    @After("execution(* com.zhangguo.Spring052.aop02.Math.*(..))")
    public void after(JoinPoint jp){
        System.out.println("----------最終通知----------");
    }
}





<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    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-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
        <context:component-scan base-package="com.zhangguo.Spring052.aop02">
        </context:component-scan>
        <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>


//測試類


package com.zhangguo.Spring052.aop02;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
     public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("aop02.xml");
            Math math = ctx.getBean("math", Math.class);
            int n1 = 100, n2 = 5;
            math.add(n1, n2);
            math.sub(n1, n2);
            math.mut(n1, n2);
            math.div(n1, n2);
        }
}

最後附上專案原始碼地址:
連結:http://pan.baidu.com/s/1hrQIdvu 密碼:eknb