1. 程式人生 > >Java Spring AOP用法

Java Spring AOP用法

切面 itl joinpoint edi long 定位 後置 spa -m

spring提供了兩個核心功能,一個是IoC(控制反轉),另外一個便是Aop(面向切面編程),IoC有助於應用對象之間的解耦,AOP則可以實現橫切關註點(如日誌、安全、緩存和事務管理)與他們所影響的對象之間的解耦。

1.簡介

AOP主要包含了通知、切點和連接點等術語,介紹如下

  • 通知(Advice)
    通知定義了切面是什麽以及何時調用,何時調用包含以下幾種

    Before 在方法被調用之前調用通知
    After 在方法完成之後調用通知,無論方法執行是否成功
    After-returning 在方法成功執行之後調用通知
    After-throwing 在方法拋出異常後調用通知
    Around 通知包裹了被通知的方法,在被通知的方法調用之前和調用之後執行自定義的行為

  • 切點(PointCut)
    通知定義了切面的什麽何時,切點定義了何處,切點的定義會匹配通知所要織入的一個或多個連接點,我們通常使用明確的類的方法名稱來指定這些切點,或是利用正則表達式定義匹配的類和方法名稱來指定這些切點。
    切點的格式如下

    execution(* com.ganji.demo.service.user.UserService.GetDemoUser (..) )

    技術分享圖片

  • 連接點(JoinPoint)
    連接點是在應用執行過程中能夠插入切面的一個點,這個點可以是調用方法時,拋出異常時,甚至是修改一個字段時,切面代碼可以利用這些連接點插入到應用的正常流程中,並添加新的行為,如日誌、安全、事務、緩存等。

現階段的AOP框架
AOP框架除了Spring AOP之外,還包括AspectJ、JBoss AOP;
上述框架的區別是Spring AOP只支持到方法連接點,另外兩個還支持字段和構造器連接點。

2.用法

同依賴註入一樣,AOP在spring中有兩種配置方式,一是xml配置的方式,二是自動註解的模式。

  • 2.1 xml中聲明切面

    • 2.1.1 AOP配置元素

    在xml中,我們使用如下AOP配置元素聲明切面

     AOP配置元素 | 描述 
    ------------ | -------------
    `<aop:advisor>` | 定義AOP通知器
    `<aop:after>`  | 定義AOP後置通知(不管該方法是否執行成功)
    `<aop:after-returning>` | 在方法成功執行後調用通知
    `<aop:after-throwing>` | 在方法拋出異常後調用通知
    `<aop:around>` | 定義AOP環繞通知
    `<aop:aspect>` | 定義切面
    `<aop:aspect-autoproxy>` | 定義`@AspectJ`註解驅動的切面
    `<aop:before>` | 定義AOP前置通知
    `<aop:config>` | 頂層的AOP配置元素,大多數的<aop:*>包含在<aop:config>元素內
    `<aop:declare-parent>` | 為被通知的對象引入額外的接口,並透明的實現
    `<aop:pointcut>` | 定義切點
    • 2.1.2 定義切面

    我們在service層添加com.ganji.demo.service.aspect.XmlAopDemoUserLog類,裏面實現了攔截方法,具體如下

    
    package com.ganji.demo.service.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    /**
     * Created by admin on 2015/9/2.
     */
    public class XmlAopDemoUserLog {
    //    方法執行前通知
        public void beforeLog() {
            System.out.println("開始執行前置通知  日誌記錄");
        }
    //    方法執行完後通知
        public void afterLog() {
            System.out.println("開始執行後置通知 日誌記錄");
        }
    //    執行成功後通知
        public void afterReturningLog() {
            System.out.println("方法成功執行後通知 日誌記錄");
        }
    //    拋出異常後通知
        public void afterThrowingLog() {
            System.out.println("方法拋出異常後執行通知 日誌記錄");
        }
    
    //    環繞通知
        public Object aroundLog(ProceedingJoinPoint joinpoint) {
            Object result = null;
            try {
                System.out.println("環繞通知開始 日誌記錄");
                long start = System.currentTimeMillis();
    
                //有返回參數 則需返回值
                result =  joinpoint.proceed();
    
                long end = System.currentTimeMillis();
                System.out.println("總共執行時長" + (end - start) + " 毫秒");
                System.out.println("環繞通知結束 日誌記錄");
            } catch (Throwable t) {
                System.out.println("出現錯誤");
            }
            return result;
        }
    }
    
    • 2.1.3 xml聲明切面並調用

    我們在web層,web-inf/dispatcher-servlet.xml中定義切面,具體如下

    
    <!--定義切面 指定攔截方法時 做什麽-->
    <bean id="xmlAopDemoUserLog" class="com.ganji.demo.service.aspect.XmlAopDemoUserLog"></bean>
    <aop:config>
        <aop:aspect ref="xmlAopDemoUserLog"> <!--指定切面-->
            <!--定義切點-->
            <aop:pointcut id="logpoint" expression="execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..))"></aop:pointcut>
            <!--定義連接點-->
            <aop:before pointcut-ref="logpoint" method="beforeLog"></aop:before>
            <aop:after pointcut-ref="logpoint" method="afterLog"></aop:after>
            <aop:after-returning pointcut-ref="logpoint" method="afterReturningLog"></aop:after-returning>
            <aop:after-throwing pointcut-ref="logpoint" method="afterThrowingLog"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

    在controller下調用,調用具體如下

    DemoUserEntity demoUser=userService.GetDemoUser(1);

    這是運行起來 我們將看到打印出如下日誌

    開始執行前置通知 日誌記錄
    開始執行後置通知 日誌記錄
    方法成功執行後通知 日誌記錄

    • 2.1.4 小結

    如果通過xml配置,我們還可以實現環繞通知,環繞通知的目的是把前置通知和後置通知的信息共享起來。同時還可以為通知傳遞方法的參數,在切面攔截中驗證參數的有效性。

  • 2.2 自動註解AOP

    在上述2.1中我們通過xml配置的形式 實現了AOP編程,現在我們通過不配置xml,配置註解的形式實現AOP。

    • 2.2.1 配置自動代理

    使用配置註解,首先我們要將切面在spring上下文中聲明成自動代理bean,我們需要在web層的web-inf/dispatcher-servlet.xml文件中配置如下一句話即可

    <aop:aspectj-autoproxy />

    當然我們需要在xml的根目錄beans下引用aop的命名空間和xsi

    
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
    • 2.2.2 使用@Aspect註解

    聲明一個切面,只需要在類名上添加@Aspect屬性即可,具體的連接點,我們用@Pointcut@Before@After等標註。具體如下
    在聲明前 我們需要依賴配置pom

    
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.11</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.6.11</version>
    </dependency>

    聲明切面類,包含了註解@Aspect以及何時(如@Before)執行通知

    
    package com.ganji.demo.service.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Service;
    
    /**
     * Created by admin on 2015/9/2.
     */
    @Aspect
    @Service
    public class XmlAopDemoUserLog {
    
    // 配置切點 及要傳的參數   
        @Pointcut("execution(* com.ganji.demo.service.user.UserService.GetDemoUser(..)) && args(id)")
        public void pointCut(int id)
        {
    
        }
    
    // 配置連接點 方法開始執行時通知
        @Before("pointCut(id)")
        public void beforeLog(int id) {
            System.out.println("開始執行前置通知  日誌記錄:"+id);
        }
    //    方法執行完後通知
        @After("pointCut(id)")
        public void afterLog(int id) {
            System.out.println("開始執行後置通知 日誌記錄:"+id);
        }
    //    執行成功後通知
        @AfterReturning("pointCut(id)")
        public void afterReturningLog(int id) {
            System.out.println("方法成功執行後通知 日誌記錄:"+id);
        }
    //    拋出異常後通知
        @AfterThrowing("pointCut(id)")
        public void afterThrowingLog(int id) {
            System.out.println("方法拋出異常後執行通知 日誌記錄"+id);
        }
    
    //    環繞通知
        @Around("pointCut(id)")
        public Object aroundLog(ProceedingJoinPoint joinpoint,int id) {
            Object result = null;
            try {
                System.out.println("環繞通知開始 日誌記錄"+id);
                long start = System.currentTimeMillis();
    
                //有返回參數 則需返回值
                result =  joinpoint.proceed();
    
                long end = System.currentTimeMillis();
                System.out.println("總共執行時長" + (end - start) + " 毫秒");
                System.out.println("環繞通知結束 日誌記錄");
            } catch (Throwable t) {
                System.out.println("出現錯誤");
            }
            return result;
        }
    }
    
    • 2.2.3 總結

    按照上述兩個步驟,使用註解實現Aop即可,這裏依賴了IOC。

Java Spring AOP用法