1. 程式人生 > >Spring--03(AOP)

Spring--03(AOP)

1.什麼是 AOP

    百度百科:在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

  1.對程式進行增強:不修改原始碼的情況下,AOP 可以進行許可權校驗,日誌記錄,效能監控,事務控制.

  2.AOP底層實現: 代理機制,Spring 的 AOP 的底層用到兩種代理機制:

            JDK 的動態代理 :針對實現了介面的類產生代理.

            Cglib 的動態代理 :針對沒有實現介面的類產生代理. 應用的是底層的位元組碼增強的技術 生成當前類的子類物件.

  3.AOP 的開發中的相關術語:

   Joinpoint(連線點):所謂連線點是指那些被攔截到的點。在 spring 中,這些點指的是方法,因為 spring 只支援方法型別的連線點.

   Pointcut(切入點):所謂切入點是指我們要對哪些 Joinpoint 進行攔截的定義.

    Advice(通知/增強):所謂通知是指攔截到 Joinpoint 之後所要做的事情就是通知.通知分為前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)

   Introduction(引介):引介是一種特殊的通知在不修改類程式碼的前提下, Introduction 可以在執行期為類動態地新增一些方法或 Field。

   Target(目標物件):代理的目標物件。

   Weaving(織入):是指把增強應用到目標物件來建立新的代理物件的過程。spring 採用動態代理織入,而 AspectJ 採用編譯期織入和類裝在期織入

   Proxy(代理):一個類被 AOP 織入增強後,就產生一個結果代理類

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

  2.AOP快速體驗

     基於上一篇部落格,根據Spring架構圖可知需要新增Spring的AOP依賴和Aspect依賴

     建立一個切面類,封裝可以對Bean進行增強的操作。

package com.wx.springsource1.aspect;

public class MyAspectXml {
    // 前置增強:在目標方法執行之前執行.
    public void before(){
        System.out.println("前置增強===========");
    }
}

    把切面類交給Spring容器管理:

<!--配置切面類-->
<bean id="myAspectXml" class="com.wx.springsource1.aspect.MyAspectXml"></bean>

 然後配置aop,對想進行增強的方法進行增強,比如現在想在訂單的save()方法前面來一個增強的操作

 <!--進行aop配置-->
    <aop:config>
        <!--配置切入點表示式:哪些類的哪些方法需要進行增強-->
        <aop:pointcut id="pointcut1" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))"></aop:pointcut>
        <!--切入面配置-->
        <aop:aspect ref="myAspectXml">
            <!--前置配置-->
            <aop:before method="before" pointcut-ref="pointcut1"></aop:before>
        </aop:aspect>
    </aop:config>

 測試:

package com.wx.springsource1.test;

import com.wx.springsource1.dao.OrderDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring1 {
    @Autowired
    private OrderDao orderDao;
    @Test
    public void testorder()
    {
        orderDao.update();
        orderDao.save();
    }
}

 在儲存訂單的前面進行了增強的操作

3.AOP的通知型別

   Aop的通知不僅僅只有前置增強,還有很多通知,如下:

     前置通知 :在目標方法執行之前執行.
     後置通知 :在目標方法執行之後執行
     環繞通知 :在目標方法執行前和執行後執行
     異常丟擲通知:在目標方法執行出現 異常的時候 執行
     最終通知 :無論目標方法是否出現異常 最終通知都會執行

 4.切入點表示式

    execution(表示式)   表示式:  [方法訪問修飾符]  方法返回值  包名.類名.方法名 (方法的引數)

public * com.wx.spring.dao.*.*(..)
* com.wx.spring.dao.*.*(..)
* com.wx.spring.dao.UserDao+.*(..)
* com.wx.spring.dao..*.*(..)

  5.現在對多個方法進行不同的增強

  <!--進行aop配置-->
    <aop:config>
        <!--配置切入點表示式:哪些類的哪些方法需要進行增強-->
        <aop:pointcut id="pointcut1" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))"></aop:pointcut>
        <aop:pointcut id="pointcut2" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.update(..))"></aop:pointcut>
        <aop:pointcut id="pointcut3" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.delete(..))"></aop:pointcut>
        <aop:pointcut id="pointcut4" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.find(..))"></aop:pointcut>
        <aop:pointcut id="pointcut5" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.importorder(..))"></aop:pointcut>
        <!--切入面配置-->
        <aop:aspect ref="myAspectXml">
            <!--前置配置-->
            <aop:before method="before" pointcut-ref="pointcut1"></aop:before>
            <!--後置配置-->
            <aop:after-returning method="afterreturning" pointcut-ref="pointcut2"></aop:after-returning>
            <!--環繞配置-->
            <aop:around method="watchPerformance" pointcut-ref="pointcut3"></aop:around>
            <!--最終通知-->
            <aop:after method="finalnotif" pointcut-ref="pointcut4"></aop:after>
            <!--異常丟擲通知-->
            <aop:after-throwing method="exceptionNotification" pointcut-ref="pointcut5"></aop:after-throwing>

        </aop:aspect>
    </aop:config>

 主要注意一下環繞配置,他需要傳入連結點,然後在連結點前後執行增強邏輯。

package com.wx.springsource1.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspectXml {
    // 前置增強:在目標方法執行之前執行.
    public void before(){
        System.out.println("前置增強===========");
    }
    //後置增強:在目標方法執行之後執行.
    public void afterreturning()
    {
        System.out.println("後置增強==========");
    }
    //環繞通知:在目標方法執行前和執行後執行
    public void watchPerformance(ProceedingJoinPoint joinpoint){
        try{
            System.out.println("begin!");
            long start = System.currentTimeMillis();

            joinpoint.proceed();

            long end = System.currentTimeMillis();
            System.out.println("end!        performance took "+(end-start)+" milliseconds");
        }catch(Throwable e){
            System.out.println("eee!We want our money back!");
        }
    }
    //最終通知:無論目標方法是否出現異常 最終通知都會 執行
    public void finalnotif()
    {
        System.out.println("最終通知==========");
    }
    //異常丟擲通知:在目標方法執行出現 異常的時候 執行
    public void exceptionNotification()
    {
        System.out.println("目標方法丟擲異常時通知==========");
    }
}
package com.wx.springsource1.daoimp;
import com.wx.springsource1.dao.OrderDao;
public class OrderDaoImp implements OrderDao {
    @Override
    public void save() {
        System.out.println("儲存訂單");
    }
    @Override
    public void update() {
        System.out.println("更新訂單");
    }
    @Override
    public void delete() {
        System.out.println("刪除訂單");
    }
    @Override
    public void find() {
        System.out.println("查詢訂單");
    }
    @Override
    public void importorder() {
        System.out.println("匯入訂單");
        throw new NullPointerException("null");
    }
}

  測試:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring1 {
    @Autowired
    private OrderDao orderDao;
    @Test
    public void testorder()
    {
        orderDao.delete();//環繞通知的時候根本沒有執行實現類的方法
        orderDao.update();
        orderDao.save();
        orderDao.find();
        orderDao.importorder();
    }
}

   

  6.AOP的註解開發方式

       1.開啟 aop 註解的自動代理

    <!--開啟 aop 註解的自動代理 aop註解開發-->
    <aop:aspectj-autoproxy/>
    <!--不管採取哪種方式開發,切面類還是要交給Spring來管理的-->
    <bean id="myAspectXml" class="com.wx.springsource1.aspect.MyAspectXml"></bean>

      2.AspectJ 的 AOP 的註解:
           @Aspect:定義切面類的註解
           通知型別:
                  * @Before :前置通知
                  * @AfterReturing :後置通知
                  * @Around :環繞通知
                  * @After :最終通知
                  * @AfterThrowing :異常丟擲通知.
           @Pointcut:定義切入點的註解

package com.wx.springsource1.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

@Aspect
public class MyAspectXml {
    @Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))")
    private void pointcut1(){}
    @Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.update(..))")
    private void pointcut2(){}
    @Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.delete(..))")
    private void pointcut3(){}
    @Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.find(..))")
    private void pointcut4(){}
    @Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.importorder(..))")
    private void pointcut5(){}
    
    
    // 前置增強:在目標方法執行之前執行.
    @Before("MyAspectXml.pointcut1()")
    public void before(){
        System.out.println("前置增強===========");
    }
    //後置增強:在目標方法執行之後執行.
    @AfterReturning("MyAspectXml.pointcut2()")
    public void afterreturning()
    {
        System.out.println("後置增強==========");
    }
    //環繞通知:在目標方法執行前和執行後執行
    @Around("MyAspectXml.pointcut3()")
    public void watchPerformance(ProceedingJoinPoint joinpoint){
        try{
            System.out.println("begin!");
            long start = System.currentTimeMillis();
            //這裡就是執行切入點的方法
            joinpoint.proceed();
            long end = System.currentTimeMillis();
            System.out.println("end!        performance took "+(end-start)+" milliseconds");
        }catch(Throwable e){
            System.out.println("eee!We want our money back!");
        }
    }
    //最終通知:無論目標方法是否出現異常 最終通知都會 執行
    @After("MyAspectXml.pointcut4()")
    public void finalnotif()
    {
        System.out.println("最終通知==========");
    }
    //異常丟擲通知:在目標方法執行出現 異常的時候 執行
    @AfterThrowing("MyAspectXml.pointcut5()")
    public void exceptionNotification()
    {
        System.out.println("目標方法丟擲異常時通知==========");
    }



}

  執行結果和XML配置完全一致