Spring AOP原理及簡單應用

來源:互聯網
上載者:User

相信只要使用過Spring架構的,大家對於AOP都不陌生,尤其提起它就能立刻隨口說出,一般用在Tlog、異常處理、許可權驗證等方面。但剛開始接觸難免會有各種各樣的疑惑,今天抽時間,按照之前的理解整理了一份關於Spring AOP的簡單教程,希望能夠協助大家儘快的瞭解它的實現過程及原理。首先來明確幾個概念:

  • JointPoint

系統在運行之前,AOP的功能模組需要織入到OOP的功能模組中。要進行這種織入過程,需要知道在系統的哪些功能點上進行織入操作,這些將要在其上進行織入操作的系統功能點就稱為JointPoint。如某方法調用的時候或者處理異常的時候,在Spring AOP中,一個連接點總是表示一個方法的執行。常見的幾種類型的JoinPoint:

Ø 方法調用:當某個方法被調用的時候所處的程式執行點;

Ø 方法執行:該類型表示的是某個方法內部執行開始時的點,應該與方法調用相區分;

Ø 構造方法調用:程式執行過程中對某個對象調用其構造方法進行初始化時的點;

Ø 構造方法執行:它與構造方法調用關係如同方法調用與方法執行間的關係;

Ø 欄位設定:對象的某個屬性通過setter方法被設定或直接被設定的執行點;

Ø 欄位擷取:某個對象相應屬性被訪問的執行點;

Ø 異常處理執行:某些類型異常拋出後,對應的異常處理邏輯執行點;

Ø 類初始化:類中某些靜態類型或靜態塊的初始化時的執行點。

  • Pointcut

Pointcut代表的是JoinPoint的表述方式。在將橫切邏輯織入當前系統的過程中,雖然知道需要在哪些功能點上織入AOP的功能模組,但需要一種表達方法。Pointcut和一個切入點運算式關聯,並在滿足這個切入點的Joinpoint上運行。目前通常使用的Pointcut方式有以下幾種:

Ø 直接指定Joinpoint所在的方法名稱;

Ø Regex,Spring的AOP支援該種方式;

Ø 使用特定的Pointcut表述語言,Spring 2.0後支援該方式。

  • Advice

Advice是單一橫切關注點邏輯的載體,它代表將會織入到JoinPoint的橫切邏輯。在切面的某個特定的連接點上執行的邏輯。根據它在Joinpoint位置執行時機的差異或完成功能的不同,可分為以下幾種形式:

Ø Before Advice:在Joinpoint指定位置之前執行的Advice類型,可以採用它來做一些系統的初始化工作,如設定系統初始值,擷取必要系統資源等。

Ø After Advice:在相應連接點之後執行的Advice類型,它還可以細分為以下三種:

² After Returning Advice:只有當前Joinpoint處執行流程正常完成後,它才會執行;

² After throwing Advice:在當前Joinpoint執行過程中拋出異常的情況下會執行;

² After Advice:該類型的Advice不管JoinPoint處執行流程是正常還是拋出異常都會執行。

Ø Around Advice:對附加其上的Joinpoint進行包裹,可以在joinpoint之前和之後都指定相應的邏輯,甚至中斷或忽略joinpoint處原來程式流程的執行。

  •  Aspect

它是對系統中橫切關注點邏輯進行模組化封裝的AOP概念實體,它可以包含多個Pointcut以及相關的Advice定義。

  •  織入器

經過織入過程後,以Aspect模組化的橫切關注點才會整合到oop的現存系統中,而完成織入過程實體稱為織入器。Spring中使用一組類來完成最終的織入操作,ProxyFactory類是Spring AOP最通用的織入器。

  •  目標對象

符合Pointcut所指定的條件,將在織入過程中被織入橫切邏輯的對象,稱之為目標對象。

單看上述的概念,可能會覺得有點眼花繚亂,其實通過一個簡單的AOP的執行個體即可以協助我們很快的瞭解其內部的機制。其實對於方法攔截有不同的實現方式,常用的即有直接採用Spring提供的各種Advice進行攔截,另一種則是採用MethodInterceptor方式進行攔截。

Spring提供的Advice攔截方式

定義一個邏輯介面IBusinessLogic:

package com.wow.asc.aop;public interface IBusinessLogic {    public void foo();    public void bar() throws BusinessLogicException;        public long time();}

其中有一個BusinessLogicException異常,它用於後面對於ThrowsAdvice進行檢驗的執行個體,在此定義為:

package com.wow.asc.aop;public class BusinessLogicException extends Exception {}

對於該商務邏輯的實現BusinessLogic,如下所示:

package com.wow.asc.aop;public class BusinessLogic implements IBusinessLogic {    @Override    public void foo() {        System.out.println("Inside BusinessLogic.foo()");    }    @Override    public void bar() throws BusinessLogicException {        System.out.println("Inside BusinessLogic.bar()");        throw new BusinessLogicException();    }     /*      * 返回該方法執行的時間     */    @Override    public long time() {        System.out.println("Inside BusinessLogic.time()");        long startTime = System.currentTimeMillis();        for(int i = 0; i < 100000000; i++);        long endTime = System.currentTimeMillis();                return (endTime - startTime);    }}

在完成上述商務邏輯編碼後,接下來將進行更多的橫切插入點的設計,如在方法執行前或返回時、拋出異常時進行各種處理。對於Advice的寫法如下所示:

package com.wow.asc.aop;import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;/** 表示一個在方法執行前進行攔截的一個Advice */public class TracingBeforeAdvice implements MethodBeforeAdvice {    @Override    public void before(Method method, Object[] args, Object target) throws Throwable {        System.out.println("execute before (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")");    }}package com.wow.asc.aop;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice;/* * 表示一個在方法返回時進行攔截的Advice*/public class TracingAfterAdvice implements AfterReturningAdvice {    @Override    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {        System.out.println(method.getDeclaringClass().getName() + "." + method.getName() + "spend time: " + returnValue);        System.out.println("execute after (by " + method.getDeclaringClass().getName() + "." + method.getName() + ")");    }}package com.wow.asc.aop;import java.lang.reflect.Method;import org.springframework.aop.ThrowsAdvice;/* * 表示一個異常拋出時進行攔截的Advice*/public class TracingThrowsAdvice implements ThrowsAdvice {        public void afterThrowing(Method method, Object[] args, Object target, Throwable subclass) {         System.out.println( "Logging that a " + subclass + "Exception was thrown.");      }}

在設計完上述的代碼及邏輯後,即可以通過applicationContext.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" xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd        http://www.springframework.org/schema/context http://localhost:8080/schema/www.springframework.org/schema/context/spring-context-2.5.xsd"><bean id="businessLogic" class="com.wow.asc.aop.BusinessLogic" /><bean id="businessLogicBean" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>com.wow.asc.aop.IBusinessLogic</value></property><property name="target"><ref local="businessLogic"/> </property><property name="interceptorNames"><list><value>theTracingBeforeAdvisor</value>                <value>theTracingAfterAdvisor</value>                <value>theTracingThrowsAdvisor</value></list></property></bean><bean id="theTracingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="advice">         <ref local="theTracingBeforeAdvice"/>      </property><property name="pattern"><value>.*</value></property></bean><bean id="theTracingAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="advice"><ref local="theTracingAfterAdvice"/></property><property name="pattern"><value>.*time.*</value></property></bean><bean id="theTracingThrowsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><property name="advice"><ref local="theTracingThrowsAdvice"/></property><property name="pattern"><value>.*bar.*</value></property></bean><bean id="theTracingBeforeAdvice" class="com.wow.asc.aop.TracingBeforeAdvice"/><bean id="theTracingAfterAdvice" class="com.wow.asc.aop.TracingAfterAdvice"/><bean id="theTracingThrowsAdvice" class="com.wow.asc.aop.TracingThrowsAdvice"/></beans>

         通過上述的配置,我們可以看出我們將IBusinessLogic做為代理介面,同時它的真正的目標類是BusinesssLogic。同時會對所有進入方法之前採用TracingBeforeAdvice進行攔截,進行方法前的預先處理;對time方法採用TracingAfterAdvice進行攔截,進行方法返回後的處理;對於bar則採用TracingThrowsAdvice進行攔截,當方法返回BusinessLogicException時進行相應的處理。

         在配置完上述類的依賴關係及需要攔截的方法後,即可以編寫用戶端程式來調用,查看它的運行機制。用戶端調用代碼:

package com.wow.asc.test;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import com.wow.asc.aop.BusinessLogicException;import com.wow.asc.aop.IBusinessLogic;public class AOPTest {    public static void main(String[] args) {        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");        IBusinessLogic ibl = (IBusinessLogic)ac.getBean("businessLogicBean");        ibl.foo();        try {            ibl.bar();        } catch (BusinessLogicException e) {            System.out.println("Caught BusinessLogicException");        }        ibl.time();    }}

        通過運行結果來詳細的瞭解下,看是否真正的如上所述,會在方法前、後及異常拋出時能夠攔截並進行相應處理。結果如下:

1、execute before (by com.wow.asc.aop.IBusinessLogic.foo)2、Inside BusinessLogic.foo()3、execute before (by com.wow.asc.aop.IBusinessLogic.bar)4、Inside BusinessLogic.bar()5、Logging that a com.wow.asc.aop.BusinessLogicExceptionException was thrown.6、Caught BusinessLogicException7、execute before (by com.wow.asc.aop.IBusinessLogic.time)8、Inside BusinessLogic.time()9、com.wow.asc.aop.IBusinessLogic.timespend time: 4610、execute after (by com.wow.asc.aop.IBusinessLogic.time)

        其實通過1、3、7行可以非常清晰的瞭解到,每個方法在執行前都被TracingBeforeAdvice攔截到,並執行了預先處理。5、6行表示當調用的是bar方法時,會被TracingThrowsAdvice攔截,當有異常拋出時,會執行相應的處理;8、9、10行則表示當調用的是time方法,返回時會被TracingAfterAdvice攔截,對其傳回值進行處理。

MethodInterceptor攔截方式

        採用該種方式進行攔截,需要實現一個繼承自MethodInterceptor的類,並將該類註冊至spring Context中,具體如下:

package com.wow.asc.aop;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;public class MyInterceptor implements MethodInterceptor {    @Override    public Object invoke(MethodInvocation invocation) {        Object result = null;        StringBuffer info = new StringBuffer();        info.append("intercept the method: ");        info.append(invocation.getMethod().getDeclaringClass().getName());        info.append(".");        info.append(invocation.getMethod().getName());        System.out.println("start " + info.toString());        try {           result = invocation.proceed();        } catch (Throwable e) {            e.printStackTrace();        } finally {            System.out.println("end " + info.toString());        }        return result;    }}

對於類的裝配,其實和上面的非常類似,樣本:

<bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean"><property name="proxyInterfaces"><value>com.wow.asc.aop.IBusinessLogic</value></property><property name="target"><ref local="businessLogic"/> </property><property name="interceptorNames"><list><value>myInterceptor</value></list></property></bean><bean id="myInterceptor" class="com.wow.asc.aop.MyInterceptor"/>

        再通過用戶端進行調用,可得到運行結果,從結果來分析可以看出它在方法執行的前、後均添加了相應的日誌。

start intercept the method: com.wow.asc.aop.IBusinessLogic.fooInside BusinessLogic.foo()end intercept the method: com.wow.asc.aop.IBusinessLogic.foostart intercept the method: com.wow.asc.aop.IBusinessLogic.timeInside BusinessLogic.time()end intercept the method: com.wow.asc.aop.IBusinessLogic.time

至此,採用兩種不同方式實現的AOP就結束了,希望大家能夠體會到其中的奧妙。

參考AOP資料:

http://www.zabada.com/technology/aop-example.html

http://onjava.com/pub/a/onjava/2004/07/14/springaop.html?page=1

http://dnizna.iteye.com/blog/1157663

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.