天天看點

Spring-02-AOP切面程式設計一.AOP的概念二.AOP面向切面程式設計的目的及意義三.Spring架構中AOP思想的應用

Spring中的AOP的使用與JDK代理/cglib代理密切相關,與代理模式相關的内容的我的上篇部落格Java設計模式–代理模式與JDK動态代理,cglib動态代理中有仔細的分析

一.AOP的概念

AOP即Aspect Oriented Programming,譯為:面向切面程式設計,AOP技術是通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP的延續,是Spring架構中的一個重要内容,也是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高開發效率

二.AOP面向切面程式設計的目的及意義

AOP即面向切面程式設計,可以說是OOP(Object Oriented Programming,面向對象程式設計)的補充和完善。OOP通過封裝、繼承、多态等概念來建立起來的對象層次結構是一種縱向的關系。但是大型項目中有很多需要進行橫向關系的抽象,比如日志代碼往往橫向地散布在所有對象層次中,而與它對應的對象的核心功能毫無關系對于其他類型的代碼,如安全性、異常處理和透明的持續性也都是如此,這種散布在各處的無關的代碼被稱為橫切,在OOP設計中,它導緻了大量代碼的重複,而不利于各個子產品的重用。

AOP技術正是用來彌補OOP不足的一項技術,AOP對項目代碼進行橫向抽取,将多個類的公共行為封裝到一個可重用子產品,并将其命名為”Aspect”,即切面,在實際項目中的展現就是那些與業務無關,卻為業務子產品所共同調用的邏輯或責任。

通過AOP實作橫向抽取後可以減少系統的重複代碼,降低子產品之間的耦合度,并有利于未來的可操作性和可維護性。

三.Spring架構中AOP思想的應用

使用了AOP思想的Spring使得開發者能在不修改源碼的情況下對程式進行功能增強。在Spring架構中,AOP切面程式設計主要用在事務處理,權限控制,日志記錄,性能監控方面

2.1 AOP程式設計的相關概念:

Joinpoint(連接配接點)

所謂連接配接點是指那些被攔截到的點。在Spring架構中,連接配接點指的是方法,因為Spring隻支援方法類型的連接配接點

Pointcut(切入點)

所謂切入點是指我們要對哪些Joinpoint進行攔截,目标類中所有的方法都是連接配接點,隻有需要進行功能增強的方法是切入點,即切入點是連接配接點的子集

Advice(通知/增強)

所謂通知是指攔截到Joinpoint之後所要做的事情就是通知。通知是切面要完成的功能,通知分為前置通知,後置通知,異常通知,最終通知,環繞通知5種。通知就是對目标方法的功能擴充,又被稱作增強

Introduction(引介)

引介是一種特殊的通知,在不修改類代碼的前提下,Introduction可以在運作期為類動态地添加一些方法或Field。

Target(目标對象)

代理對象的目标對象

Proxy(代理)

一個類被AOP織入增強後,就産生一個結果代理類

Aspect(切面)

是切入點和通知(引介)的結合

Weaving(織入)

目标對象進行功能增強并建立代理對象的過程。Spring采用動态代理(包括JDK代理和cglib代理)進行織入,而AspectJ采用編譯期織入和類裝載期織入

2.2 使用Spring架構模拟AOP式事務處理過程:

2.2.1 模拟過程需要三步:

1)編寫業務元件

2)編寫通知,即對業務元件的增強功能。同時在Spring配置檔案中配置通知

3)定義切入點,一個切入點可能橫切多個業務元件

2.2.2 模拟程式:

1)業務層,用來處理業務邏輯

public interface UserService {
    void save();
}
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("儲存使用者!");
    }
}
           

2)通知,通知的作用是對目标類進行增強

public class MyAdvice {
    //前置通知
    public void before(){
        System.out.println("這是前置通知!!");
    }
    //後置通知
    public void afterReturning(){
        System.out.println("這是後置通知(如果出現異常不會調用)!!");
    }
    //環繞通知,方法的參數是ProceedingJoinPoint,即連接配接點,在這裡連接配接點是目标類
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("這是環繞通知之前的部分!!");
        //調用目标方法
        Object proceed = pjp.proceed();
        System.out.println("這是環繞通知之後的部分!!");
        return proceed;
    }
    //異常通知
    public void afterException(){
        System.out.println("出事啦!出現異常了!!");
    }
    //後置通知
    public void after(){
        System.out.println("這是後置通知(出現異常也會調用)!!");
    }
}
           

3)Spring配置檔案,使用IOC和DI管理業務類,并配置切點和通知

<!-- 1.配置目标對象 -->
<bean name="userService" class="cn.spring.aop.service.UserServiceImpl" ></bean>
<!-- 2.配置通知對象 -->
<bean name="myAdvice" class="cn.spring.aop.e_annotationaop.MyAdvice" ></bean>
<!-- 3.配置切入點及通知 -->
<aop:config>
        <!-- 配置切入點 -->
        <!-- 沒有指定具體的方法,是以ServiceImpl類中所有的方法都是切入點,即對ServiceImpl類中所有的方法進行增強 -->
        <aop:pointcut expression="execution(* cn.spring.aop.service.*ServiceImpl.*(..))" id="pc"/>
        <!-- 配置通知 -->
        <aop:aspect ref="myAdvice" >
            <!-- 配置前置通知,一個名稱為before的方法-->
            <aop:before method="before" pointcut-ref="pc" />
            <!-- 配置後置通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pc" />
            <!-- 配置環繞通知 -->
            <aop:around method="around" pointcut-ref="pc" />
            <!-- 配置異常通知 -->
            <aop:after-throwing method="afterException" pointcut-ref="pc"/>
            <!-- 配置後置通知 -->
            <aop:after method="after" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
</beans>
           

3)測試類

//用于測試的注解
@RunWith(SpringJUnit4ClassRunner.class)、
//加載Spring配置檔案
@ContextConfiguration("classpath:applicationContext_3.xml")
public class AOPTest {
    //注入(DI)一個userService類的執行個體
    @Resource(name="userService")
    private UserService us;
    //測試test()方法
    @Test
    public void test(){
        us.save();
    }
}
           

2.3 AOP事務處理過程解析:

2.3.1 切入點配置(切入點表達式)

使用Spring架構進行AOP程式設計時,要在Spring配置檔案中配置切入點表達式,切入點表達式用來和目标類中的方法進行比對,比對成功的方法為切入點,切入點表達式的寫法如下:

1)比對cn.spring.service包下的UserServiceImpl類的名稱為save的方法,方法必須是是public共有方法,沒有方法參數,且傳回值為void

public void cn.spring.service.UserServiceImpl.save()
           

2)比對cn.spring.service包下的UserServiceImpl類的名稱為save的方法,方法沒有方法參數,且傳回值為void,可以為共有或私有

void cn.spring.service.UserServiceImpl.save()
           

3)比對cn.spring.service包下的UserServiceImpl類的名稱為save的方法,方法沒有方法參數,且傳回值可以為任意類型,可以為共有或私有

* cn.spring.service.UserServiceImpl.save()
           

4)比對cn.spring.service包下的UserServiceImpl類的所有方法,方法沒有方法參數,且傳回值可以為任意類型,可以為共有或私有

* cn.spring.service.UserServiceImpl.*()
           

5)比對cn.spring.service包下的名稱中帶有ServiceImpl的類(即可以是UserServiceImpl類,也可以是OrderServiceImpl類)的所有方法,方法參數任意,且傳回值可以為任意類型,可以為共有或私有

* cn.spring.service.*ServiceImpl.*(..)
           

6)比對cn.spring.service包及其子包下的名稱中帶有ServiceImpl的類的方法(即可以是UserServiceImpl,也可以是OrderServiceImpl),方法參數任意,且傳回值可以為任意類型,可以為共有或私有

* cn.spring.service..*ServiceImpl.*(..)
           

2.3.2 Spring架構實作AOP程式設計的方式

Spring架構内部使用動态代理來實作AOP程式設計,其中動态代理由兩種方式,JDK動态代理和cglib動态代理,使用哪種動态代理由Spring架構自動選擇,也可以在Spring的配置檔案中設定使用JDK動态代理或cglib動态代理,将标簽

<aop:config>

中的屬性“proxy-target-class”設定為true則會使用cglib動态代理,設定為false則會使用JDK動态代理。使用過程中一般不會設定此屬性,由Spring自動識别并選擇