1. 程式人生 > >spring之AOP原始碼深入理解(一)aop攔截

spring之AOP原始碼深入理解(一)aop攔截

(一)  原始碼角度

攔截機 (Interceptor), 是 AOP (Aspect-Oriented Programming) 的另一種叫法。AOP本身是一門語言,只不過我們使用的是基於JAVA的整合到Spring 中的 SpringAOP。同樣,我們將通過我們的例子來理解陌生的概念。

1、介面類

package com.test.TestSpring3;  
  
public interface UserService // 被攔截的介面  
...{  
    public void printUser(String user);  
}  

 2、實現類

package com.test.TestSpring3;  
​
public class UserServiceImp implements UserService // 實現UserService介面  
...{  
    public void printUser(String user) ...{  
        System.out.println("printUser user:" + user);// 顯示user  
    }  
}  

3、 AOP攔截器

package com.test.TestSpring3;  
  
import org.aopalliance.intercept.MethodInterceptor;  
import org.aopalliance.intercept.MethodInvocation;  
  
public class UserInterceptor implements MethodInterceptor  
// AOP方法攔截器  
...{  
  
    public Object invoke(MethodInvocation arg0) throws Throwable ...{  
  
        try ...{  
  
            if (arg0.getMethod().getName().equals("printUser"))  
            // 攔截方法是否是UserService介面的printUser方法  
            ...{  
                Object[] args = arg0.getArguments();// 被攔截的引數  
                System.out.println("user:" + args[0]);  
                arg0.getArguments()[0] = "hello!";// 修改被攔截的引數  
  
            }  
  
            System.out.println(arg0.getMethod().getName() + "---!");  
            return arg0.proceed();// 執行UserService介面的printUser方法  
  
        } catch (Exception e) ...{  
            throw e;  
        }  
    }  
}  

 

(二)spring aop Aspect攔截器註解開發

    Spring AOP 與ApectJ 的目的一致,都是為了統一處理橫切業務,但與AspectJ不同的是,Spring AOP 並不嘗試提供完整的AOP功能(即使它完全可以實現),Spring AOP 更注重的是與Spring IOC容器的結合,並結合該優勢來解決橫切業務的問題,因此在AOP的功能完善方面,相對來說AspectJ具有更大的優勢,同時,Spring注意到AspectJ在AOP的實現方式上依賴於特殊編譯器(ajc編譯器),因此Spring很機智迴避了這點,轉向採用動態代理技術的實現原理來構建Spring AOP的內部機制(動態織入),這是與AspectJ(靜態織入)最根本的區別。在AspectJ 1.5後,引入@Aspect形式的註解風格的開發,Spring也非常快地跟進了這種方式,因此Spring 2.0後便使用了與AspectJ一樣的註解。請注意,Spring 只是使用了與 AspectJ 5 一樣的註解,但仍然沒有使用 AspectJ 的編譯器,底層依是動態代理技術的實現,因此並不依賴於 AspectJ 的編譯器。下面我們先通過一個簡單的案例來演示Spring AOP的入門程式。

 

1、定義目標類介面和實現類:

 //介面類
public interface UserDao {

    int addUser();

    void updateUser();

    void deleteUser();

    void findUser();
}

 

//實現類
import com.zejian.spring.springAop.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImp implements UserDao {
    @Override
    public int addUser() {
        System.out.println("add user ......");
        return 6666;
    }

    @Override
    public void updateUser() {
        System.out.println("update user ......");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete user ......");
    }

    @Override
    public void findUser() {
        System.out.println("find user ......");
    }
}

 2、使用Spring 2.0引入的註解方式,編寫Spring AOP的aspect 類:

@Aspect
public class MyAspect {

    /**
     * 前置通知
     */
    @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 後置通知
     * returnVal,切點方法執行後的返回值
     */
    @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
    public void AfterReturning(Object returnVal){
        System.out.println("後置通知...."+returnVal);
    }


    /**
     * 環繞通知
     * @param joinPoint 可用於執行切點的類
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("環繞通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("環繞通知後....");
        return obj;
    }

    /**
     * 丟擲通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
        System.out.println("出現異常:msg="+e.getMessage());
    }

    /**
     * 無論什麼情況下都會執行的方法
     */
    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void after(){
        System.out.println("最終通知....");
    }
}

3、編寫配置檔案交由Spring IOC容器管理:

<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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 啟動@aspectj的自動代理支援-->
    <aop:aspectj-autoproxy />

    <!-- 定義目標物件 -->
    <bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
    <!-- 定義aspect類 -->
    <bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>

簡單說明一下,定義了一個目標類UserDaoImpl,利用Spring2.0引入的aspect註解開發功能定義aspect類即MyAspect,在該aspect類中,編寫了5種註解型別的通知函式,分別是前置通知@Before、後置通知@AfterReturning、環繞通知@Around、異常通知@AfterThrowing、最終通知@After,這5種通知與前面分析AspectJ的通知型別幾乎是一樣的,並註解通知上使用execution關鍵字定義的切點表示式,即指明該通知要應用的目標函式,當只有一個execution引數時,value屬性可以省略,當含兩個以上的引數,value必須註明,如存在返回值時。當然除了把切點表示式直接傳遞給通知註解型別外,還可以使用@pointcut來定義切點匹配表示式,這個與AspectJ使用關鍵字pointcut是一樣的,後面分析。目標類和aspect類定義完成後,最後需要在xml配置檔案中進行配置,同樣的所有類的建立都交由SpringIOC容器處理,注意,使用Spring AOP 的aspectJ功能時,需要使用以下程式碼啟動aspect的註解功能支援:

<aop:aspectj-autoproxy />